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;
014import java.util.*;
015import javax.annotation.*;
016import javax.swing.*;
017import javax.swing.border.Border;
018import jmri.*;
019import jmri.NamedBean.DisplayOptions;
020import jmri.implementation.DefaultConditionalAction;
021import jmri.jmrit.blockboss.BlockBossLogic;
022import jmri.jmrit.blockboss.BlockBossLogicProvider;
023import jmri.jmrit.catalog.NamedIcon;
024import jmri.jmrit.display.*;
025import jmri.jmrit.signalling.SignallingGuiTools;
026import jmri.swing.NamedBeanComboBox;
027import jmri.util.JmriJFrame;
028import jmri.util.MathUtil;
029import jmri.util.swing.JComboBoxUtil;
030
031/**
032 * Layout Editor Tools provides tools making use of layout connectivity
033 * available in Layout Editor panels.
034 * <p>
035 * The tools in this module are accessed via the Tools menu in Layout Editor.
036 *
037 * @author Dave Duchamp Copyright (c) 2007
038 * @author George Warner Copyright (c) 2017-2019
039 */
040final public class LayoutEditorTools {
041
042    //constants
043    //private final int NONE = 0;  //Signal at Turnout Positions
044    //operational instance variables shared between tools
045    private LayoutEditor layoutEditor = null;
046    private MultiIconEditor signalIconEditor = null;
047    private JFrame signalFrame = null;
048    private boolean needRedraw = false;
049    private BlockBossLogic logic = null;
050    private SignalHead auxSignal = null;
051
052    //constructor method
053    public LayoutEditorTools(@Nonnull LayoutEditor thePanel) {
054        layoutEditor = thePanel;
055
056        //Turnouts
057        LayoutEditor.setupComboBox(sensorsTurnoutComboBox, true, true, false);
058        LayoutEditor.setupComboBox(signalMastsTurnoutComboBox, true, true, false);
059        LayoutEditor.setupComboBox(turnout1ComboBox, true, true, false);
060        LayoutEditor.setupComboBox(turnout2ComboBox, true, true, false);
061        LayoutEditor.setupComboBox(turnoutAComboBox, true, true, false);
062        LayoutEditor.setupComboBox(turnoutBComboBox, true, true, false);
063        LayoutEditor.setupComboBox(turnoutComboBox, true, true, false);
064
065        //Blocks
066        LayoutEditor.setupComboBox(block1IDComboBox, true, true, false);
067        LayoutEditor.setupComboBox(block2IDComboBox, true, true, false);
068        LayoutEditor.setupComboBox(blockACComboBox, true, true, false);
069        LayoutEditor.setupComboBox(blockBDComboBox, true, true, false);
070        LayoutEditor.setupComboBox(slipSensorsBlockAComboBox, true, true, false);
071        LayoutEditor.setupComboBox(slipSensorsBlockBComboBox, true, true, false);
072        LayoutEditor.setupComboBox(slipSensorsBlockCComboBox, true, true, false);
073        LayoutEditor.setupComboBox(slipSensorsBlockDComboBox, true, true, false);
074        LayoutEditor.setupComboBox(slipSignalBlockAComboBox, true, true, false);
075        LayoutEditor.setupComboBox(slipSignalBlockBComboBox, true, true, false);
076        LayoutEditor.setupComboBox(slipSignalBlockCComboBox, true, true, false);
077        LayoutEditor.setupComboBox(slipSignalBlockDComboBox, true, true, false);
078        LayoutEditor.setupComboBox(xingBlockACComboBox, true, true, false);
079        LayoutEditor.setupComboBox(xingBlockBDComboBox, true, true, false);
080        LayoutEditor.setupComboBox(xingSensorsBlockACComboBox, true, true, false);
081        LayoutEditor.setupComboBox(xingSensorsBlockBDComboBox, true, true, false);
082
083        //Signal Heads
084        LayoutEditor.setupComboBox(a1_3WaySignalHeadComboBox, true, true, false);
085        LayoutEditor.setupComboBox(a1SignalHeadComboBox, true, true, false);
086        LayoutEditor.setupComboBox(a1SlipSignalHeadComboBox, true, true, false);
087        LayoutEditor.setupComboBox(a1TToTSignalHeadComboBox, true, true, false);
088        LayoutEditor.setupComboBox(a2_3WaySignalHeadComboBox, true, true, false);
089        LayoutEditor.setupComboBox(a2SignalHeadComboBox, true, true, false);
090        LayoutEditor.setupComboBox(a2SlipSignalHeadComboBox, true, true, false);
091        LayoutEditor.setupComboBox(a2TToTSignalHeadComboBox, true, true, false);
092        LayoutEditor.setupComboBox(a3_3WaySignalHeadComboBox, true, true, false);
093        LayoutEditor.setupComboBox(aSignalHeadComboBox, true, true, false);
094        LayoutEditor.setupComboBox(b_3WaySignalHeadComboBox, true, true, false);
095        LayoutEditor.setupComboBox(b1SignalHeadComboBox, true, true, false);
096        LayoutEditor.setupComboBox(b1SlipSignalHeadComboBox, true, true, false);
097        LayoutEditor.setupComboBox(b1TToTSignalHeadComboBox, true, true, false);
098        LayoutEditor.setupComboBox(b2SignalHeadComboBox, true, true, false);
099        LayoutEditor.setupComboBox(b2SlipSignalHeadComboBox, true, true, false);
100        LayoutEditor.setupComboBox(b2TToTSignalHeadComboBox, true, true, false);
101        LayoutEditor.setupComboBox(bSignalHeadComboBox, true, true, false);
102        LayoutEditor.setupComboBox(c_3WaySignalHeadComboBox, true, true, false);
103        LayoutEditor.setupComboBox(c1SignalHeadComboBox, true, true, false);
104        LayoutEditor.setupComboBox(c1SlipSignalHeadComboBox, true, true, false);
105        LayoutEditor.setupComboBox(c1TToTSignalHeadComboBox, true, true, false);
106        LayoutEditor.setupComboBox(c2SignalHeadComboBox, true, true, false);
107        LayoutEditor.setupComboBox(c2SlipSignalHeadComboBox, true, true, false);
108        LayoutEditor.setupComboBox(c2TToTSignalHeadComboBox, true, true, false);
109        LayoutEditor.setupComboBox(continuingSignalHeadComboBox, false, true, false);
110        LayoutEditor.setupComboBox(cSignalHeadComboBox, true, true, false);
111        LayoutEditor.setupComboBox(d_3WaySignalHeadComboBox, true, true, false);
112        LayoutEditor.setupComboBox(d1SignalHeadComboBox, true, true, false);
113        LayoutEditor.setupComboBox(d1SlipSignalHeadComboBox, true, true, false);
114        LayoutEditor.setupComboBox(d1TToTSignalHeadComboBox, true, true, false);
115        LayoutEditor.setupComboBox(d2SignalHeadComboBox, true, true, false);
116        LayoutEditor.setupComboBox(d2SlipSignalHeadComboBox, true, true, false);
117        LayoutEditor.setupComboBox(d2TToTSignalHeadComboBox, true, true, false);
118        LayoutEditor.setupComboBox(divergingSignalHeadComboBox, false, true, false);
119        LayoutEditor.setupComboBox(dSignalHeadComboBox, true, true, false);
120        LayoutEditor.setupComboBox(eastBoundSignalHeadComboBox, true, true, false);
121        LayoutEditor.setupComboBox(throatContinuingSignalHeadComboBox, false, true, false);
122        LayoutEditor.setupComboBox(throatDivergingSignalHeadComboBox, false, true, false);
123        LayoutEditor.setupComboBox(westBoundSignalHeadComboBox, true, true, false);
124
125        // TODO: Set combobox exclude lists for turnouts, blocks and signal heads
126        // that are not part of the current layout panel
127    }
128
129    /*=====================*\
130    |* setSignalsAtTurnout *|
131    \*=====================*/
132    /**
133     * Tool to set signals at a turnout, including placing the signal icons and
134     * optionally setup of Simple Signal Logic for each signal head
135     * <p>
136     * This tool assumes left facing signal head icons have been selected, and
137     * will rotate the signal head icons accordingly.
138     * <p>
139     * This tool will place throat icons on the right side of the track, and
140     * continuing and diverging icons on the outside edge of the turnout.
141     */
142    //operational variables for Set Signals at Turnout tool
143    private JmriJFrame setSignalsAtTurnoutFrame = null;
144    private boolean setSignalsAtTurnoutOpenFlag = false;
145    private boolean setSignalsAtTurnoutFromMenuFlag = false;
146
147    private JLabel turnoutNameLabel = null;
148
149    private final NamedBeanComboBox<Turnout> turnoutComboBox = new NamedBeanComboBox<>(
150            InstanceManager.turnoutManagerInstance(),
151            null, DisplayOptions.DISPLAYNAME);
152
153    private final NamedBeanComboBox<SignalHead> throatContinuingSignalHeadComboBox = new NamedBeanComboBox<>(
154            InstanceManager.getDefault(SignalHeadManager.class),
155            null, DisplayOptions.DISPLAYNAME);
156    private final NamedBeanComboBox<SignalHead> throatDivergingSignalHeadComboBox = new NamedBeanComboBox<>(
157            InstanceManager.getDefault(SignalHeadManager.class),
158            null, DisplayOptions.DISPLAYNAME);
159    private final NamedBeanComboBox<SignalHead> continuingSignalHeadComboBox = new NamedBeanComboBox<>(
160            InstanceManager.getDefault(SignalHeadManager.class),
161            null, DisplayOptions.DISPLAYNAME);
162    private final NamedBeanComboBox<SignalHead> divergingSignalHeadComboBox = new NamedBeanComboBox<>(
163            InstanceManager.getDefault(SignalHeadManager.class),
164            null, DisplayOptions.DISPLAYNAME);
165
166    private final JCheckBox setPlaceAllHeads = new JCheckBox(Bundle.getMessage("PlaceAllHeads"));
167    private final JCheckBox setupAllLogic = new JCheckBox(Bundle.getMessage("SetAllLogic"));
168
169    private final JCheckBox setThroatContinuing = new JCheckBox(Bundle.getMessage("PlaceHead"));
170    private final JCheckBox setupLogicThroatContinuing = new JCheckBox(Bundle.getMessage("SetLogic"));
171    private final JCheckBox setThroatDiverging = new JCheckBox(Bundle.getMessage("PlaceHead"));
172    private final JCheckBox setupLogicThroatDiverging = new JCheckBox(Bundle.getMessage("SetLogic"));
173    private final JCheckBox setContinuing = new JCheckBox(Bundle.getMessage("PlaceHead"));
174    private final JCheckBox setupLogicContinuing = new JCheckBox(Bundle.getMessage("SetLogic"));
175    private final JCheckBox setDiverging = new JCheckBox(Bundle.getMessage("PlaceHead"));
176    private final JCheckBox setupLogicDiverging = new JCheckBox(Bundle.getMessage("SetLogic"));
177    private JButton getSavedSignalHeads = null;
178    private JButton changeSignalIcon = null;
179    private JButton setSignalsDone = null;
180    private JButton setSignalsCancel = null;
181
182    private LayoutTurnout layoutTurnout = null;
183    private double placeSignalDirectionDEG = 0.0;
184
185    private Turnout turnout = null;
186    private SignalHead throatContinuingHead = null;
187    private SignalHead throatDivergingHead = null;
188    private SignalHead continuingHead = null;
189    private SignalHead divergingHead = null;
190
191    //display dialog for Set Signals at Turnout tool
192    public void setSignalsAtTurnoutFromMenu(@Nonnull LayoutTurnout to,
193            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
194        layoutTurnout = to;
195        turnout = to.getTurnout();
196        turnoutComboBox.setSelectedItem(to.getTurnout());
197        setSignalsAtTurnoutFromMenuFlag = true;
198        setSignalsAtTurnout(theEditor, theFrame);
199        setSignalsAtTurnoutFromMenuFlag = false;
200    }
201
202    public void setSignalsAtTurnout(@Nonnull MultiIconEditor theEditor,
203            @Nonnull JFrame theFrame) {
204        signalIconEditor = theEditor;
205        signalFrame = theFrame;
206
207        //Initialize if needed
208        if (setSignalsAtTurnoutFrame == null) {
209            setSignalsAtTurnoutOpenFlag = false;
210            setSignalsAtTurnoutFrame = new JmriJFrame(Bundle.getMessage("SignalsAtTurnout"), false, true);
211            oneFrameToRuleThemAll(setSignalsAtTurnoutFrame);
212            setSignalsAtTurnoutFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
213
214            setSignalsAtTurnoutFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtTurnout", true);
215            setSignalsAtTurnoutFrame.setLocation(70, 30);
216            Container theContentPane = setSignalsAtTurnoutFrame.getContentPane();
217            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
218
219            JPanel panel1 = new JPanel(new FlowLayout());
220            turnoutNameLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanNameTurnout")));
221            panel1.add(turnoutNameLabel);
222            panel1.add(turnoutComboBox);
223            turnoutNameLabel.setLabelFor(turnoutComboBox);
224            turnoutComboBox.setToolTipText(Bundle.getMessage("SignalsTurnoutNameHint"));
225            theContentPane.add(panel1);
226            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
227
228            JPanel panel2 = new JPanel(new FlowLayout());
229            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
230            panel2.add(shTitle);
231            panel2.add(new JLabel("   "));
232            panel2.add(getSavedSignalHeads = new JButton(Bundle.getMessage("GetSaved")));
233            getSavedSignalHeads.addActionListener(this::turnoutSignalsGetSaved);
234            getSavedSignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
235            theContentPane.add(panel2);
236
237            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
238            JPanel panel2a = new JPanel(new FlowLayout());
239            panel2a.add(new JLabel("   "));
240            panel2a.add(setPlaceAllHeads);
241            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
242            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
243                boolean isSelected = setPlaceAllHeads.isSelected();
244                //(de)select all checkboxes
245                setThroatContinuing.setSelected(isSelected);
246                setThroatDiverging.setSelected(isSelected);
247                setContinuing.setSelected(isSelected);
248                setDiverging.setSelected(isSelected);
249            });
250            panel2a.add(new JLabel("  "));
251            panel2a.add(setupAllLogic);
252            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
253            setupAllLogic.addActionListener((ActionEvent e) -> {
254                boolean isSelected = setupAllLogic.isSelected();
255                //(de)select all checkboxes
256                setupLogicThroatContinuing.setSelected(isSelected);
257                setupLogicThroatDiverging.setSelected(isSelected);
258                setupLogicContinuing.setSelected(isSelected);
259                setupLogicDiverging.setSelected(isSelected);
260            });
261            theContentPane.add(panel2a);
262
263            JPanel panel21 = new JPanel(new FlowLayout());
264            JLabel throatContinuingLabel = new JLabel(
265                    Bundle.getMessage("MakeLabel", throatContinuingString));
266            panel21.add(throatContinuingLabel);
267            panel21.add(throatContinuingSignalHeadComboBox);
268            throatContinuingLabel.setLabelFor(throatContinuingSignalHeadComboBox);
269            theContentPane.add(panel21);
270            throatContinuingSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
271
272            JPanel panel22 = new JPanel(new FlowLayout());
273            panel22.add(new JLabel("   "));
274            panel22.add(setThroatContinuing);
275            setThroatContinuing.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
276            panel22.add(new JLabel("  "));
277            panel22.add(setupLogicThroatContinuing);
278            setupLogicThroatContinuing.setToolTipText(Bundle.getMessage("SetLogicHint"));
279            theContentPane.add(panel22);
280
281            JPanel panel31 = new JPanel(new FlowLayout());
282            JLabel throatDivergingLabel = new JLabel(
283                    Bundle.getMessage("MakeLabel", throatDivergingString));
284            panel31.add(throatDivergingLabel);
285            panel31.add(throatDivergingSignalHeadComboBox);
286            throatDivergingLabel.setLabelFor(throatDivergingSignalHeadComboBox);
287            theContentPane.add(panel31);
288            throatDivergingSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
289
290            JPanel panel32 = new JPanel(new FlowLayout());
291            panel32.add(new JLabel("   "));
292            panel32.add(setThroatDiverging);
293            setThroatDiverging.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
294            panel32.add(new JLabel("  "));
295            panel32.add(setupLogicThroatDiverging);
296            setupLogicThroatDiverging.setToolTipText(Bundle.getMessage("SetLogicHint"));
297            theContentPane.add(panel32);
298
299            JPanel panel41 = new JPanel(new FlowLayout());
300            JLabel continuingLabel = new JLabel(
301                    Bundle.getMessage("MakeLabel", continuingString));
302            panel41.add(continuingLabel);
303            panel41.add(continuingSignalHeadComboBox);
304            continuingLabel.setLabelFor(continuingSignalHeadComboBox);
305            theContentPane.add(panel41);
306            continuingSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
307
308            JPanel panel42 = new JPanel(new FlowLayout());
309            panel42.add(new JLabel("   "));
310            panel42.add(setContinuing);
311            setContinuing.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
312            panel42.add(new JLabel("  "));
313            panel42.add(setupLogicContinuing);
314            setupLogicContinuing.setToolTipText(Bundle.getMessage("SetLogicHint"));
315            theContentPane.add(panel42);
316
317            JPanel panel51 = new JPanel(new FlowLayout());
318            JLabel divergingLabel = new JLabel(
319                    Bundle.getMessage("MakeLabel", divergingString));
320            panel51.add(divergingLabel);
321            panel51.add(divergingSignalHeadComboBox);
322            divergingLabel.setLabelFor(divergingSignalHeadComboBox);
323            theContentPane.add(panel51);
324            divergingSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
325
326            JPanel panel52 = new JPanel(new FlowLayout());
327            panel52.add(new JLabel("   "));
328            panel52.add(setDiverging);
329            setDiverging.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
330            panel52.add(new JLabel("  "));
331            panel52.add(setupLogicDiverging);
332            setupLogicDiverging.setToolTipText(Bundle.getMessage("SetLogicHint"));
333            theContentPane.add(panel52);
334            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
335
336            JPanel panel6 = new JPanel(new FlowLayout());
337            panel6.add(changeSignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
338            changeSignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
339            changeSignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
340            panel6.add(new JLabel("   "));
341            panel6.add(setSignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
342            setSignalsDone.addActionListener(this::setSignalsDonePressed);
343            setSignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
344
345            panel6.add(setSignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
346            setSignalsCancel.addActionListener(this::setSignalsCancelPressed);
347            setSignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
348            theContentPane.add(panel6);
349            setSignalsAtTurnoutFrame.addWindowListener(new WindowAdapter() {
350                @Override
351                public void windowClosing(WindowEvent e) {
352                    setSignalsCancelPressed(null);
353                }
354            });
355        }
356        setPlaceAllHeads.setSelected(false);
357        setupAllLogic.setSelected(false);
358
359        turnoutComboBox.setVisible(!setSignalsAtTurnoutFromMenuFlag);
360        String turnoutLabelString = Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanNameTurnout"));
361        if (setSignalsAtTurnoutFromMenuFlag) {
362            turnoutNameLabel.setText(turnoutLabelString + layoutTurnout.getTurnoutName());
363            turnoutSignalsGetSaved(null);
364        } else {
365            turnoutNameLabel.setText(turnoutLabelString);
366        }
367
368        if (!setSignalsAtTurnoutOpenFlag) {
369            setSignalsAtTurnoutFrame.setPreferredSize(null);
370            setSignalsAtTurnoutFrame.pack();
371            setSignalsAtTurnoutOpenFlag = true;
372        }
373        setSignalsAtTurnoutFrame.setVisible(true);
374    }   //setSignalsAtTurnout
375
376    private void turnoutSignalsGetSaved(ActionEvent a) {
377        if (getTurnoutInformation(false)) {
378            throatContinuingSignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalA1());
379            throatDivergingSignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalA2());
380            continuingSignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalB1());
381            divergingSignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalC1());
382        }
383    }
384
385    private void setSignalsCancelPressed(ActionEvent a) {
386        setSignalsAtTurnoutOpenFlag = false;
387        setSignalsAtTurnoutFrame.setVisible(false);
388    }
389
390    private void setSignalsDonePressed(ActionEvent a) {
391        //process turnout name
392        if (!getTurnoutInformation(false)) {
393            return;
394        }
395        //process signal head names
396        if (!getTurnoutSignalHeadInformation()) {
397            return;
398        }
399        //place signals as requested
400        String signalHeadName = throatContinuingSignalHeadComboBox.getSelectedItemDisplayName();
401        if (signalHeadName == null) {
402            signalHeadName = "";
403        }
404        if (setThroatContinuing.isSelected()) {
405            if (isHeadOnPanel(throatContinuingHead)
406                    && (throatContinuingHead != getHeadFromName(layoutTurnout.getSignalA1Name()))) {
407                JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
408                        Bundle.getMessage("SignalsError6",
409                                new Object[]{signalHeadName}),
410                        Bundle.getMessage("ErrorTitle"),
411                        JOptionPane.ERROR_MESSAGE);
412                return;
413            } else {
414                removeSignalHeadFromPanel(layoutTurnout.getSignalA1Name());
415                placeThroatContinuing();
416                removeAssignment(throatContinuingHead);
417                layoutTurnout.setSignalA1Name(signalHeadName);
418                needRedraw = true;
419            }
420        } else {
421            LayoutTurnout.Geometry assigned = isHeadAssignedHere(throatContinuingHead, layoutTurnout);
422            if (assigned == LayoutTurnout.Geometry.NONE) {
423                if (isHeadOnPanel(throatContinuingHead)
424                        && isHeadAssignedAnywhere(throatContinuingHead)) {
425                    JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
426                            Bundle.getMessage("SignalsError8",
427                                    new Object[]{signalHeadName}),
428                            Bundle.getMessage("ErrorTitle"),
429                            JOptionPane.ERROR_MESSAGE);
430                    return;
431                } else {
432                    removeSignalHeadFromPanel(layoutTurnout.getSignalA1Name());
433                    removeAssignment(throatContinuingHead);
434                    layoutTurnout.setSignalA1Name(signalHeadName);
435                }
436                //} else if (assigned != A1) {
437                //TODO: need to figure out what to do in this case
438                //assigned to a different position on the same turnout.
439                //}
440            }
441        }
442        signalHeadName = throatDivergingSignalHeadComboBox.getSelectedItemDisplayName();
443        if (signalHeadName == null) {
444            signalHeadName = "";
445        }
446        if ((setThroatDiverging.isSelected()) && (throatDivergingHead != null)) {
447            if (isHeadOnPanel(throatDivergingHead)
448                    && (throatDivergingHead != getHeadFromName(layoutTurnout.getSignalA2Name()))) {
449                JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
450                        Bundle.getMessage("SignalsError6",
451                                new Object[]{signalHeadName}),
452                        Bundle.getMessage("ErrorTitle"),
453                        JOptionPane.ERROR_MESSAGE);
454                return;
455            } else {
456                removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
457                placeThroatDiverging();
458                removeAssignment(throatDivergingHead);
459                layoutTurnout.setSignalA2Name(signalHeadName);
460                needRedraw = true;
461            }
462        } else if (throatDivergingHead != null) {
463            LayoutTurnout.Geometry assigned = isHeadAssignedHere(throatDivergingHead, layoutTurnout);
464            if (assigned == LayoutTurnout.Geometry.NONE) {
465                if (isHeadOnPanel(throatDivergingHead)
466                        && isHeadAssignedAnywhere(throatDivergingHead)) {
467                    JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
468                            Bundle.getMessage("SignalsError8",
469                                    new Object[]{signalHeadName}),
470                            Bundle.getMessage("ErrorTitle"),
471                            JOptionPane.ERROR_MESSAGE);
472                    return;
473                } else {
474                    removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
475                    removeAssignment(throatDivergingHead);
476                    layoutTurnout.setSignalA2Name(signalHeadName);
477                }
478                //} else if (assigned != A2) {
479                //need to figure out what to do in this case - assigned to a different position on the same turnout.
480            }
481        } else {   //throatDivergingHead is always null here
482            removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
483            layoutTurnout.setSignalA2Name("");
484        }
485
486        signalHeadName = continuingSignalHeadComboBox.getSelectedItemDisplayName();
487        if (signalHeadName == null) {
488            signalHeadName = "";
489        }
490        if (setContinuing.isSelected()) {
491            if (isHeadOnPanel(continuingHead)
492                    && (continuingHead != getHeadFromName(layoutTurnout.getSignalB1Name()))) {
493                JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
494                        Bundle.getMessage("SignalsError6",
495                                new Object[]{signalHeadName}),
496                        Bundle.getMessage("ErrorTitle"),
497                        JOptionPane.ERROR_MESSAGE);
498                return;
499            } else {
500                removeSignalHeadFromPanel(layoutTurnout.getSignalB1Name());
501                if (layoutTurnout.getContinuingSense() == Turnout.CLOSED) {
502                    placeContinuing(signalHeadName);
503                } else {
504                    placeDiverging(signalHeadName);
505                }
506                removeAssignment(continuingHead);
507                layoutTurnout.setSignalB1Name(signalHeadName);
508                needRedraw = true;
509            }
510        } else {
511            LayoutTurnout.Geometry assigned = isHeadAssignedHere(continuingHead, layoutTurnout);
512            if (assigned == LayoutTurnout.Geometry.NONE) {
513                if (isHeadOnPanel(continuingHead)
514                        && isHeadAssignedAnywhere(continuingHead)) {
515                    JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
516                            Bundle.getMessage("SignalsError8",
517                                    new Object[]{signalHeadName}),
518                            Bundle.getMessage("ErrorTitle"),
519                            JOptionPane.ERROR_MESSAGE);
520                    return;
521                } else {
522                    removeSignalHeadFromPanel(layoutTurnout.getSignalB1Name());
523                    removeAssignment(continuingHead);
524                    layoutTurnout.setSignalB1Name(signalHeadName);
525                }
526                //} else if (assigned != B1) {
527                //need to figure out what to do in this case - assigned to a different position on the same turnout.
528            }
529        }
530
531        signalHeadName = divergingSignalHeadComboBox.getSelectedItemDisplayName();
532        if (signalHeadName == null) {
533            signalHeadName = "";
534        }
535        if (setDiverging.isSelected()) {
536            if (isHeadOnPanel(divergingHead)
537                    && (divergingHead != getHeadFromName(layoutTurnout.getSignalC1Name()))) {
538                JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
539                        Bundle.getMessage("SignalsError6",
540                                new Object[]{signalHeadName}),
541                        Bundle.getMessage("ErrorTitle"),
542                        JOptionPane.ERROR_MESSAGE);
543                return;
544            } else {
545                removeSignalHeadFromPanel(layoutTurnout.getSignalC1Name());
546                if (layoutTurnout.getContinuingSense() == Turnout.CLOSED) {
547                    placeDiverging(signalHeadName);
548                } else {
549                    placeContinuing(signalHeadName);
550                }
551                removeAssignment(divergingHead);
552                layoutTurnout.setSignalC1Name(signalHeadName);
553                needRedraw = true;
554            }
555        } else {
556            LayoutTurnout.Geometry assigned = isHeadAssignedHere(divergingHead, layoutTurnout);
557            if (assigned == LayoutTurnout.Geometry.NONE) {
558                if (isHeadOnPanel(divergingHead)
559                        && isHeadAssignedAnywhere(divergingHead)) {
560                    JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
561                            Bundle.getMessage("SignalsError8",
562                                    new Object[]{signalHeadName}),
563                            Bundle.getMessage("ErrorTitle"),
564                            JOptionPane.ERROR_MESSAGE);
565                    return;
566                } else {
567                    removeSignalHeadFromPanel(layoutTurnout.getSignalC1Name());
568                    removeAssignment(divergingHead);
569                    layoutTurnout.setSignalC1Name(signalHeadName);
570                }
571                //} else if (assigned != C1) {
572                //need to figure out what to do in this case - assigned to a different position on the same turnout.
573            }
574        }
575        //setup Logic if requested and enough information is available
576        if (setupLogicThroatContinuing.isSelected()) {
577            setLogicThroatContinuing();
578        }
579        if ((throatDivergingHead != null) && setupLogicThroatDiverging.isSelected()) {
580            setLogicThroatDiverging();
581        }
582        if (setupLogicContinuing.isSelected()) {
583            setLogicContinuing();
584        }
585        if (setupLogicDiverging.isSelected()) {
586            setLogicDiverging();
587        }
588        //make sure this layout turnout is not linked to another
589        layoutTurnout.setLinkType(LayoutTurnout.LinkType.NO_LINK);
590        layoutTurnout.setLinkedTurnoutName("");
591        //finish up
592        setSignalsAtTurnoutOpenFlag = false;
593        setSignalsAtTurnoutFrame.setVisible(false);
594        if (needRedraw) {
595            layoutEditor.redrawPanel();
596            layoutEditor.setDirty();
597            needRedraw = false;
598        }
599    }   //setSignalsDonePressed
600
601    /**
602     * Checks the turnout name (could also be a crossover, ...)
603     * 1) checks setSignalsAtTurnoutFromMenuFlag (bis setSignalsAtXoverTurnoutFromMenuFlag)
604     *      skip to 6 if true
605     * 2) get a name string from turnoutComboBox (bis NamedBean.normalizeUserName(xoverTurnoutName) ),
606     *      showing an error dialog if not present
607     * 3) Gets turnout by that name, showing a error and returning false if not
608     * 4) (??) if the turnout's user name is non-existant or not matching, reset the turnout name string source
609     *              used in 2 (does this ever work?)
610     * 5) Search through all LayoutTurnout (and subclass) objects, looking for a match. If
611     *          is the other type (LayoutTurnout vs XOver or vice-versa), so an error,
612     *          call a cancel routine and return false. Save the found item in the
613     *          'layoutTurnout' non-local variable
614     *
615     * 6) If the above succeed in finding a layoutTurnout, calculate an angle and
616     *          store in the `placeSignalDirectionDEG` non-local variable
617     *          and return true (success)
618     * 7) Finally, show an error saying the turnout is not displayed on this panel and return false.
619     *
620     * In summary, this makes some checks, and then (re)loads the 'layoutTurnout' and
621     * 'placeSignalDirectionDEG' non-local variables, returning true for success
622     *
623     * @return true if ok, false if not for various reasons
624     */
625    private boolean getTurnoutInformation(boolean isCrossover) {
626        String str = "";
627        if (isCrossover ? !setSignalsAtXoverTurnoutFromMenuFlag : !setSignalsAtTurnoutFromMenuFlag) {
628            turnout = null;
629            layoutTurnout = null;
630            str = isCrossover ? NamedBean.normalizeUserName(xoverTurnoutName)
631                    : turnoutComboBox.getSelectedItemDisplayName();
632            if ((str == null) || str.isEmpty()) {
633                JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
634                        Bundle.getMessage("SignalsError1"),
635                        Bundle.getMessage("ErrorTitle"),
636                        JOptionPane.ERROR_MESSAGE);
637                return false;
638            }
639            turnout = InstanceManager.turnoutManagerInstance().getTurnout(str);
640            if (turnout == null) {
641                JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
642                        Bundle.getMessage("SignalsError2",
643                                new Object[]{str}), Bundle.getMessage("ErrorTitle"),
644                        JOptionPane.ERROR_MESSAGE);
645                return false;
646            } else {
647                String uname = turnout.getUserName();
648                if ((uname == null) || uname.isEmpty() || !uname.equals(str)) {
649                    if (isCrossover) {
650                        xoverTurnoutName = str;
651                    } else {
652                        turnoutComboBox.setSelectedItem(turnout);
653                    }
654                }
655            }
656            for (LayoutTurnout t : layoutEditor.getLayoutTurnouts()) {
657                if (t.getTurnout() == turnout) {
658                    layoutTurnout = t;
659                    if (t.isTurnoutTypeXover() != isCrossover) {
660                        if (isCrossover) {
661                            JOptionPane.showMessageDialog(layoutEditor,
662                                    Bundle.getMessage("InfoMessage8"),
663                                    Bundle.getMessage("MessageTitle"),
664                                    JOptionPane.INFORMATION_MESSAGE);
665                            setXoverSignalsCancelPressed(null);
666                        } else {
667                            JOptionPane.showMessageDialog(layoutEditor,
668                                    Bundle.getMessage("InfoMessage1"),
669                                    Bundle.getMessage("MessageTitle"),
670                                    JOptionPane.INFORMATION_MESSAGE);
671                            setSignalsCancelPressed(null);
672                        }
673                        return false;
674                    }
675                    break;
676                }
677            }
678        }
679
680        if (layoutTurnout != null) {
681            // convert to view to get angle on screen display
682            LayoutTurnoutView ltv = layoutEditor.getLayoutTurnoutView(layoutTurnout);
683
684            Point2D coordsA = ltv.getCoordsA(), coords2;
685            if (isCrossover) {
686                coords2 = ltv.getCoordsB();
687            } else {
688                coords2 = ltv.getCoordsCenter();
689            }
690            placeSignalDirectionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coords2, coordsA));
691            return true;
692        }
693        JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
694                Bundle.getMessage("SignalsError3",
695                        new Object[]{str}), Bundle.getMessage("ErrorTitle"),
696                JOptionPane.ERROR_MESSAGE);
697        return false;
698    }   //getTurnoutInformation
699
700    private boolean getTurnoutSignalHeadInformation() {
701        throatContinuingHead = getSignalHeadFromEntry(throatContinuingSignalHeadComboBox, true, setSignalsAtTurnoutFrame);
702        if (throatContinuingHead == null) {
703            return false;
704        }
705        throatDivergingHead = getSignalHeadFromEntry(throatDivergingSignalHeadComboBox, false, setSignalsAtTurnoutFrame);
706        continuingHead = getSignalHeadFromEntry(continuingSignalHeadComboBox, true, setSignalsAtTurnoutFrame);
707        if (continuingHead == null) {
708            return false;
709        }
710        divergingHead = getSignalHeadFromEntry(divergingSignalHeadComboBox, true, setSignalsAtTurnoutFrame);
711        if (divergingHead == null) {
712            return false;
713        }
714        return true;
715    }
716    private NamedIcon testIcon = null;
717
718    private void placeThroatContinuing() {
719        if (testIcon == null) {
720            testIcon = signalIconEditor.getIcon(0);
721        }
722        String signalHeadName = throatContinuingSignalHeadComboBox.getSelectedItemDisplayName();
723        if (signalHeadName == null) {
724            signalHeadName = "";
725        }
726        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
727
728        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
729
730        Point2D coordsA = layoutTurnoutView.getCoordsA();
731        Point2D delta = new Point2D.Double(+shift, +shift);
732
733        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
734        Point2D where = MathUtil.add(coordsA, delta);
735        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
736    }
737
738    private void placeThroatDiverging() {
739        if (testIcon == null) {
740            testIcon = signalIconEditor.getIcon(0);
741        }
742        String signalHeadName = throatDivergingSignalHeadComboBox.getSelectedItemDisplayName();
743        if (signalHeadName == null) {
744            signalHeadName = "";
745        }
746        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
747
748        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
749
750        Point2D coordsA = layoutTurnoutView.getCoordsA();
751        Point2D delta = new Point2D.Double(-shift, +shift);
752
753        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
754        Point2D where = MathUtil.add(coordsA, delta);
755        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
756    }
757
758    private void placeContinuing(@Nonnull String signalHeadName) {
759        if (testIcon == null) {
760            testIcon = signalIconEditor.getIcon(0);
761        }
762        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
763
764        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
765
766        Point2D coordsB = layoutTurnoutView.getCoordsB();
767        Point2D coordsC = layoutTurnoutView.getCoordsC();
768        Point2D coordsCenter = layoutTurnoutView.getCoordsCenter();
769
770        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
771        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
772        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
773        double shiftX = 0.0;
774        if (diffDirDEG < 0.0) {
775            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
776        }
777        Point2D delta = new Point2D.Double(shiftX, -shift);
778
779        delta = MathUtil.rotateDEG(delta, bDirDEG);
780        Point2D where = MathUtil.add(coordsB, delta);
781        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
782    }
783
784    private void placeDiverging(String signalHeadName) {
785        if (testIcon == null) {
786            testIcon = signalIconEditor.getIcon(0);
787        }
788        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
789
790        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
791
792        Point2D coordsB = layoutTurnoutView.getCoordsB();
793        Point2D coordsC = layoutTurnoutView.getCoordsC();
794        Point2D coordsCenter = layoutTurnoutView.getCoordsCenter();
795
796        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
797        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
798        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
799        double shiftX = 0.0;
800        if (diffDirDEG >= 0.0) {
801            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
802        }
803        Point2D delta = new Point2D.Double(shiftX, -shift);
804
805        delta = MathUtil.rotateDEG(delta, cDirDEG);
806        Point2D where = MathUtil.add(coordsC, delta);
807        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
808    }
809
810    private void setLogicThroatContinuing() {
811        TrackSegment track = null;
812        if (layoutTurnout.getContinuingSense() == Turnout.CLOSED) {
813            track = (TrackSegment) layoutTurnout.getConnectB();
814        } else {
815            track = (TrackSegment) layoutTurnout.getConnectC();
816        }
817        if (track == null) {
818            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
819                    Bundle.getMessage("InfoMessage7"),
820                    Bundle.getMessage("MessageTitle"),
821                    JOptionPane.INFORMATION_MESSAGE);
822            return;
823        }
824        LayoutBlock block = track.getLayoutBlock();
825        if (block == null) {
826            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
827                    Bundle.getMessage("InfoMessage6"),
828                    Bundle.getMessage("MessageTitle"),
829                    JOptionPane.INFORMATION_MESSAGE);
830            return;
831        }
832        Sensor occupancy = block.getOccupancySensor();
833        if (occupancy == null) {
834            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
835                    Bundle.getMessage("InfoMessage4",
836                            new Object[]{block.getUserName()}),
837                    Bundle.getMessage("MessageTitle"),
838                    JOptionPane.INFORMATION_MESSAGE);
839            return;
840        }
841
842        String signalHeadName = throatContinuingSignalHeadComboBox.getSelectedItemDisplayName();
843        if (signalHeadName == null) {
844            signalHeadName = "";
845        }
846        SignalHead nextHead = getNextSignalFromObject(track,
847                layoutTurnout, signalHeadName, setSignalsAtTurnoutFrame);
848        if ((nextHead == null) && (!reachedEndBumper())) {
849            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
850                    Bundle.getMessage("InfoMessage5",
851                            new Object[]{block.getUserName()}),
852                    Bundle.getMessage("MessageTitle"),
853                    JOptionPane.INFORMATION_MESSAGE);
854            return;
855        }
856        if (throatDivergingHead != null) {
857            if (!initializeBlockBossLogic(signalHeadName)) {
858                return;
859            }
860            logic.setMode(BlockBossLogic.TRAILINGMAIN);
861            logic.setTurnout(turnout.getDisplayName());
862            logic.setSensor1(occupancy.getDisplayName());
863            if (nextHead != null) {
864                logic.setWatchedSignal1(nextHead.getDisplayName(), false);
865            }
866            if (auxSignal != null) {
867                logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
868            }
869            finalizeBlockBossLogic();
870            return;
871        }
872        SignalHead savedAuxSignal = auxSignal;
873        TrackSegment track2 = null;
874        if (layoutTurnout.getContinuingSense() == Turnout.CLOSED) {
875            track2 = (TrackSegment) layoutTurnout.getConnectC();
876        } else {
877            track2 = (TrackSegment) layoutTurnout.getConnectB();
878        }
879        if (track2 == null) {
880            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
881                    Bundle.getMessage("InfoMessage7"),
882                    Bundle.getMessage("MessageTitle"),
883                    JOptionPane.INFORMATION_MESSAGE);
884            return;
885        }
886        LayoutBlock block2 = track2.getLayoutBlock();
887        if (block2 == null) {
888            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
889                    Bundle.getMessage("InfoMessage6"),
890                    Bundle.getMessage("MessageTitle"),
891                    JOptionPane.INFORMATION_MESSAGE);
892            return;
893        }
894        Sensor occupancy2 = block2.getOccupancySensor();
895        if (occupancy2 == null) {
896            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
897                    Bundle.getMessage("InfoMessage4",
898                            new Object[]{block2.getUserName()}),
899                    Bundle.getMessage("MessageTitle"),
900                    JOptionPane.INFORMATION_MESSAGE);
901            return;
902        }
903        signalHeadName = throatContinuingSignalHeadComboBox.getSelectedItemDisplayName();
904        if (signalHeadName == null) {
905            signalHeadName = "";
906        }
907        SignalHead nextHead2 = getNextSignalFromObject(track2,
908                layoutTurnout, signalHeadName, setSignalsAtTurnoutFrame);
909        if ((nextHead2 == null) && (!reachedEndBumper())) {
910            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
911                    Bundle.getMessage("InfoMessage5",
912                            new Object[]{block2.getUserName()}),
913                    Bundle.getMessage("MessageTitle"),
914                    JOptionPane.INFORMATION_MESSAGE);
915            return;
916        }
917        if (!initializeBlockBossLogic(signalHeadName)) {
918            return;
919        }
920        logic.setMode(BlockBossLogic.FACING);
921        logic.setTurnout(turnout.getDisplayName());
922        logic.setWatchedSensor1(occupancy.getDisplayName());
923        logic.setWatchedSensor2(occupancy2.getDisplayName());
924        if (nextHead != null) {
925            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
926        }
927        if (savedAuxSignal != null) {
928            logic.setWatchedSignal1Alt(savedAuxSignal.getDisplayName());
929        }
930        if (nextHead2 != null) {
931            logic.setWatchedSignal2(nextHead2.getDisplayName());
932        }
933        if (auxSignal != null) {
934            logic.setWatchedSignal2Alt(auxSignal.getDisplayName());
935        }
936        if (!layoutTurnout.isMainlineC()) {
937            logic.setLimitSpeed2(true);
938        }
939        finalizeBlockBossLogic();
940    }   //setLogicThroatContinuing
941
942    private void setLogicThroatDiverging() {
943        TrackSegment track = null;
944        if (layoutTurnout.getContinuingSense() == Turnout.CLOSED) {
945            track = (TrackSegment) layoutTurnout.getConnectC();
946        } else {
947            track = (TrackSegment) layoutTurnout.getConnectB();
948        }
949        if (track == null) {
950            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
951                    Bundle.getMessage("InfoMessage7"),
952                    Bundle.getMessage("MessageTitle"),
953                    JOptionPane.INFORMATION_MESSAGE);
954            return;
955        }
956        LayoutBlock block = track.getLayoutBlock();
957        if (block == null) {
958            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
959                    Bundle.getMessage("InfoMessage6"),
960                    Bundle.getMessage("MessageTitle"),
961                    JOptionPane.INFORMATION_MESSAGE);
962            return;
963        }
964        Sensor occupancy = block.getOccupancySensor();
965        if (occupancy == null) {
966            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
967                    Bundle.getMessage("InfoMessage4",
968                            new Object[]{block.getUserName()}),
969                    Bundle.getMessage("MessageTitle"),
970                    JOptionPane.INFORMATION_MESSAGE);
971            return;
972        }
973        String signalHeadName = throatDivergingSignalHeadComboBox.getSelectedItemDisplayName();
974        if (signalHeadName == null) {
975            signalHeadName = "";
976        }
977        SignalHead nextHead = getNextSignalFromObject(track,
978                layoutTurnout, signalHeadName, setSignalsAtTurnoutFrame);
979        if ((nextHead == null) && (!reachedEndBumper())) {
980            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
981                    Bundle.getMessage("InfoMessage5",
982                            new Object[]{block.getUserName()}),
983                    Bundle.getMessage("MessageTitle"),
984                    JOptionPane.INFORMATION_MESSAGE);
985            return;
986        }
987        if (!initializeBlockBossLogic(signalHeadName)) {
988            return;
989        }
990
991        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
992        logic.setTurnout(turnout.getDisplayName());
993        logic.setSensor1(occupancy.getDisplayName());
994        if (nextHead != null) {
995            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
996        }
997        if (auxSignal != null) {
998            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
999        }
1000        if (!layoutTurnout.isMainlineC()) {
1001            logic.setLimitSpeed2(true);
1002        }
1003        finalizeBlockBossLogic();
1004    }   //setLogicThroatDiverging
1005
1006    private void setLogicContinuing() {
1007        TrackSegment track = (TrackSegment) layoutTurnout.getConnectA();
1008        if (track == null) {
1009            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1010                    Bundle.getMessage("InfoMessage7"),
1011                    Bundle.getMessage("MessageTitle"),
1012                    JOptionPane.INFORMATION_MESSAGE);
1013            return;
1014        }
1015        LayoutBlock block = track.getLayoutBlock();
1016        if (block == null) {
1017            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1018                    Bundle.getMessage("InfoMessage6"),
1019                    Bundle.getMessage("MessageTitle"),
1020                    JOptionPane.INFORMATION_MESSAGE);
1021            return;
1022        }
1023        Sensor occupancy = block.getOccupancySensor();
1024        if (occupancy == null) {
1025            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1026                    Bundle.getMessage("InfoMessage4",
1027                            new Object[]{block.getUserName()}),
1028                    Bundle.getMessage("MessageTitle"),
1029                    JOptionPane.INFORMATION_MESSAGE);
1030            return;
1031        }
1032        String signalHeadName = continuingSignalHeadComboBox.getSelectedItemDisplayName();
1033        if (signalHeadName == null) {
1034            signalHeadName = "";
1035        }
1036        SignalHead nextHead = getNextSignalFromObject(track,
1037                layoutTurnout, signalHeadName, setSignalsAtTurnoutFrame);
1038        if ((nextHead == null) && (!reachedEndBumper())) {
1039            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1040                    Bundle.getMessage("InfoMessage5",
1041                            new Object[]{block.getUserName()}),
1042                    Bundle.getMessage("MessageTitle"),
1043                    JOptionPane.INFORMATION_MESSAGE);
1044            return;
1045        }
1046        if (!initializeBlockBossLogic(signalHeadName)) {
1047            return;
1048        }
1049        logic.setMode(BlockBossLogic.TRAILINGMAIN);
1050        logic.setTurnout(turnout.getDisplayName());
1051        logic.setSensor1(occupancy.getDisplayName());
1052        if (nextHead != null) {
1053            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
1054        }
1055        if (auxSignal != null) {
1056            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
1057        }
1058        finalizeBlockBossLogic();
1059    }   //setLogicContinuing
1060
1061    private void setLogicDiverging() {
1062        TrackSegment track = (TrackSegment) layoutTurnout.getConnectA();
1063        if (track == null) {
1064            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1065                    Bundle.getMessage("InfoMessage7"),
1066                    Bundle.getMessage("MessageTitle"),
1067                    JOptionPane.INFORMATION_MESSAGE);
1068            return;
1069        }
1070        LayoutBlock block = track.getLayoutBlock();
1071        if (block == null) {
1072            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1073                    Bundle.getMessage("InfoMessage6"),
1074                    Bundle.getMessage("MessageTitle"),
1075                    JOptionPane.INFORMATION_MESSAGE);
1076            return;
1077        }
1078        Sensor occupancy = block.getOccupancySensor();
1079        if (occupancy == null) {
1080            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1081                    Bundle.getMessage("InfoMessage4",
1082                            new Object[]{block.getUserName()}),
1083                    Bundle.getMessage("MessageTitle"),
1084                    JOptionPane.INFORMATION_MESSAGE);
1085            return;
1086        }
1087        String signalHeadName = divergingSignalHeadComboBox.getSelectedItemDisplayName();
1088        if (signalHeadName == null) {
1089            signalHeadName = "";
1090        }
1091        SignalHead nextHead = getNextSignalFromObject(track,
1092                layoutTurnout, signalHeadName, setSignalsAtTurnoutFrame);
1093        if ((nextHead == null) && (!reachedEndBumper())) {
1094            JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1095                    Bundle.getMessage("InfoMessage5",
1096                            new Object[]{block.getUserName()}),
1097                    Bundle.getMessage("MessageTitle"),
1098                    JOptionPane.INFORMATION_MESSAGE);
1099            return;
1100        }
1101        if (!initializeBlockBossLogic(signalHeadName)) {
1102            return;
1103        }
1104        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
1105        logic.setTurnout(turnout.getDisplayName());
1106        logic.setSensor1(occupancy.getDisplayName());
1107        if (nextHead != null) {
1108            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
1109        }
1110        if (auxSignal != null) {
1111            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
1112        }
1113        if (!layoutTurnout.isMainlineC()) {
1114            logic.setLimitSpeed2(true);
1115        }
1116        finalizeBlockBossLogic();
1117    }   //setLogicDiverging
1118
1119    /*==========================================*\
1120    | * Utility routines used by multiple tools *|
1121    \*==========================================*/
1122    /**
1123     * Returns the layout turnout corresponding to a given turnout.
1124     * <p>
1125     * If require
1126     * double crossover is requested, an error message is sent to the user if
1127     * the layout turnout is not a double crossover, and null is returned.
1128     * <p>
1129     * If a layout turnout corresponding to the turnout is not found, an error
1130     * message is sent to the user and null is returned.
1131     *
1132     * @param turnout the base turnout.
1133     * @param requireDoubleXover true to force checking of turnout type.
1134     * @param str error message string.
1135     * @param theFrame main frame.
1136     * @return layout turnout, may be null.
1137     */
1138    @CheckReturnValue
1139    public LayoutTurnout getLayoutTurnoutFromTurnout(
1140            @Nonnull Turnout turnout,
1141            boolean requireDoubleXover,
1142            @Nonnull String str,
1143            @CheckForNull JFrame theFrame) {
1144        for (LayoutTurnout t : layoutEditor.getLayoutTurnouts()) {
1145            if (t.getTurnout() == turnout) {
1146                //have the layout turnout corresponding to the turnout
1147                if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER)
1148                        && (!requireDoubleXover)) {
1149                    JOptionPane.showMessageDialog(theFrame,
1150                            Bundle.getMessage("InfoMessage1"),
1151                            Bundle.getMessage("MessageTitle"),
1152                            JOptionPane.INFORMATION_MESSAGE);
1153                    return null;
1154                }
1155                if (requireDoubleXover && (t.getTurnoutType() != LayoutTurnout.TurnoutType.DOUBLE_XOVER)) {
1156                    JOptionPane.showMessageDialog(theFrame,
1157                            Bundle.getMessage("InfoMessage8"),
1158                            Bundle.getMessage("MessageTitle"),
1159                            JOptionPane.INFORMATION_MESSAGE);
1160                    return null;
1161                }
1162                return t;
1163            }
1164        }
1165        //layout turnout not found
1166        JOptionPane.showMessageDialog(theFrame,
1167                Bundle.getMessage("SignalsError3",
1168                        new Object[]{str}), Bundle.getMessage("ErrorTitle"),
1169                JOptionPane.ERROR_MESSAGE);
1170        return null;
1171    }
1172
1173    /**
1174     * Returns the SignalHead corresponding to an entry field in the specified
1175     * dialog. This also takes care of UpperCase and trimming of leading and
1176     * trailing blanks. If entry is required, and no entry is present, and error
1177     * message is sent. An error message also results if a signal head with the
1178     * entered name is not found in the SignalTable.
1179     * @param signalNameComboBox the combo box with signal name selected.
1180     * @param requireEntry true if mandatory field, else false.
1181     * @param frame the main frame.
1182     * @return signal head, may be null.
1183     */
1184    @CheckReturnValue
1185    public SignalHead getSignalHeadFromEntry(
1186            @Nonnull NamedBeanComboBox<SignalHead> signalNameComboBox,
1187            boolean requireEntry,
1188            @Nonnull JmriJFrame frame) {
1189        String signalName = signalNameComboBox.getSelectedItemDisplayName();
1190        SignalHead result = getSignalHeadFromEntry(signalName, requireEntry, frame);
1191        if (result != null) {
1192            String uname = result.getUserName();
1193            if ((uname == null) || uname.isEmpty() || !uname.equals(signalName)) {
1194                signalNameComboBox.setSelectedItem(result);
1195            }
1196        }
1197        return result;
1198    }
1199
1200    @CheckReturnValue
1201    public SignalHead getSignalHeadFromEntry(
1202            @Nonnull JTextField signalNameTextField,
1203            boolean requireEntry, @Nonnull JmriJFrame frame) {
1204        String signalName = NamedBean.normalizeUserName(signalNameTextField.getText());
1205        SignalHead result = getSignalHeadFromEntry(signalName, requireEntry, frame);
1206        if (result != null) {
1207            String uname = result.getUserName();
1208            if ((uname == null) || uname.isEmpty() || !uname.equals(signalName)) {
1209                signalNameTextField.setText(signalName);
1210            }
1211        }
1212        return result;
1213    }
1214
1215    @CheckReturnValue
1216    public SignalHead getSignalHeadFromEntry(@CheckForNull String signalName,
1217            boolean requireEntry, @Nonnull JmriJFrame frame) {
1218        if ((signalName == null) || signalName.isEmpty()) {
1219            if (requireEntry) {
1220                JOptionPane.showMessageDialog(frame, Bundle.getMessage("SignalsError5"),
1221                        Bundle.getMessage("ErrorTitle"),
1222                        JOptionPane.ERROR_MESSAGE);
1223            }
1224            return null;
1225        }
1226        SignalHead head = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1227        if (head == null) {
1228            JOptionPane.showMessageDialog(frame,
1229                    Bundle.getMessage("SignalsError4",
1230                            new Object[]{signalName}), Bundle.getMessage("ErrorTitle"),
1231                    JOptionPane.ERROR_MESSAGE);
1232            return null;
1233        }
1234        return (head);
1235    }
1236
1237    /**
1238     * Returns a SignalHead given a name.
1239     * @param str signal head name.
1240     * @return signal head, may be null.
1241     */
1242    @CheckReturnValue
1243    public SignalHead getHeadFromName(@CheckForNull String str) {
1244        SignalHead result = null;
1245        if ((str != null) && !str.isEmpty()) {
1246            result = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(str);
1247        }
1248        return result;
1249    }
1250
1251    /**
1252     * Places a signal head icon on the panel after rotation at the designated
1253     * place, with all icons taken care of.
1254     *
1255     * @param directionDEG   rotation in degrees.
1256     * @param signalHeadName name of a signal head.
1257     * @param where          coordinates for placing signal head on panel.
1258     */
1259    public void setSignalHeadOnPanel(double directionDEG,
1260            @Nonnull String signalHeadName,
1261            @Nonnull Point2D where) {
1262        setSignalHeadOnPanel(directionDEG, signalHeadName, (int) where.getX(), (int) where.getY());
1263    }
1264
1265    /**
1266     * Places a signal head icon on the panel after rotation at the designated
1267     * place, with all icons taken care of.
1268     *
1269     * @param directionDEG   rotation in degrees.
1270     * @param signalHeadName name of a signal head.
1271     * @param xLoc           x coordinate for placing signal head on panel.
1272     * @param yLoc           y coordinate for placing signal head on panel.
1273     */
1274    public void setSignalHeadOnPanel(double directionDEG, @Nonnull String signalHeadName, int xLoc, int yLoc) {
1275        SignalHeadIcon l = getSignalHeadIcon(signalHeadName);
1276
1277        if (directionDEG > 0) {
1278            Iterator<String> e = l.getIconStateNames();
1279            while (e.hasNext()) {
1280                l.getIcon(e.next()).rotate((int) directionDEG, l);
1281            }
1282        }
1283
1284        l.setLocation(xLoc - (int) (l.maxWidth() / 2.0), yLoc - (int) (l.maxHeight() / 2.0));
1285
1286        layoutEditor.putSignal(l);
1287    }
1288
1289    /**
1290     * Returns an index if the specified signal head is assigned to the
1291     * LayoutTurnout initialized. Otherwise returns the NONE index. The index
1292     * specifies the turnout position of the signal head according to the code
1293     * listed at the beginning of this module.
1294     */
1295    private LayoutTurnout.Geometry isHeadAssignedHere(@Nonnull SignalHead head, @Nonnull LayoutTurnout lTurnout) {
1296        LayoutTurnout.Geometry result = LayoutTurnout.Geometry.NONE;
1297
1298        Map<String, LayoutTurnout.Geometry> map = new HashMap<>();
1299        map.put(lTurnout.getSignalA1Name(), LayoutTurnout.Geometry.POINTA1);
1300        map.put(lTurnout.getSignalA2Name(), LayoutTurnout.Geometry.POINTA2);
1301        map.put(lTurnout.getSignalA3Name(), LayoutTurnout.Geometry.POINTA3);
1302        map.put(lTurnout.getSignalB1Name(), LayoutTurnout.Geometry.POINTB1);
1303        map.put(lTurnout.getSignalB2Name(), LayoutTurnout.Geometry.POINTB2);
1304        map.put(lTurnout.getSignalC1Name(), LayoutTurnout.Geometry.POINTC1);
1305        map.put(lTurnout.getSignalC2Name(), LayoutTurnout.Geometry.POINTC2);
1306        map.put(lTurnout.getSignalD1Name(), LayoutTurnout.Geometry.POINTD1);
1307        map.put(lTurnout.getSignalD2Name(), LayoutTurnout.Geometry.POINTD2);
1308
1309        String sName = head.getSystemName();
1310        String uName = head.getUserName();
1311
1312        for (Map.Entry<String, LayoutTurnout.Geometry> entry : map.entrySet()) {
1313            String signalName = entry.getKey();
1314
1315            if (!signalName.isEmpty() && (signalName.equals(sName) || signalName.equals(uName))) {
1316                result = entry.getValue();
1317                break;
1318            }
1319        }
1320
1321        return result;
1322    }
1323
1324    /**
1325     * Get if signal head is on the panel.
1326     * @param head the signal head to locate.
1327     * @return true if an icon for the specified SignalHead is on the panel.
1328     */
1329    public boolean isHeadOnPanel(@Nonnull SignalHead head) {
1330        for (SignalHeadIcon h : layoutEditor.getSignalList()) {
1331            if (h.getSignalHead() == head) {
1332                return true;
1333            }
1334        }
1335        return false;
1336    }
1337
1338    /**
1339     * Returns true if the specified Signal Head is assigned to an object on the
1340     * panel, regardless of whether an icon is displayed or not.
1341     * @param head the signal head to locate.
1342     * @return true if the signal head is attached to a panel object.
1343     */
1344    public boolean isHeadAssignedAnywhere(@Nonnull SignalHead head) {
1345        String sName = head.getSystemName();
1346        String uName = head.getUserName();
1347
1348        for (LayoutTurnout to : layoutEditor.getLayoutTurnouts()) {
1349            if (isHeadAssignedHere(head, to) != LayoutTurnout.Geometry.NONE) {
1350                return true;
1351            }
1352        }
1353
1354        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
1355            if ((po.getEastBoundSignal().equals(sName) || ((uName != null)
1356                    && (po.getEastBoundSignal().equals(uName))))) {
1357                return true;
1358            }
1359            if ((po.getWestBoundSignal().equals(sName) || ((uName != null)
1360                    && (po.getWestBoundSignal().equals(uName))))) {
1361                return true;
1362            }
1363        }
1364
1365        for (LevelXing x : layoutEditor.getLevelXings()) {
1366            if (  (x.getSignalAName().equals(sName) || ((uName != null)
1367                    && (x.getSignalAName().equals(uName))))) {
1368                return true;
1369            }
1370            if (  (x.getSignalBName().equals(sName) || ((uName != null)
1371                    && (x.getSignalBName().equals(uName))))) {
1372                return true;
1373            }
1374            if (  (x.getSignalCName().equals(sName) || ((uName != null)
1375                    && (x.getSignalCName().equals(uName))))) {
1376                return true;
1377            }
1378            if (  (x.getSignalDName().equals(sName) || ((uName != null)
1379                    && (x.getSignalDName().equals(uName))))) {
1380                return true;
1381            }
1382        }
1383        return false;
1384    }   //isHeadAssignedAnywhere
1385
1386    /**
1387     * Removes the assignment of the specified SignalHead to either a turnout, a
1388     * positionable point, or a level crossing wherever it is assigned.
1389     * @param head the signal head to be removed.
1390     */
1391    public void removeAssignment(@Nonnull SignalHead head) {
1392        String sName = head.getSystemName();
1393        String uName = head.getUserName();
1394        for (LayoutTurnout to : layoutEditor.getLayoutTurnouts()) {
1395            if ((to.getSignalA1Name().equals(sName) || ((uName != null)
1396                    && to.getSignalA1Name().equals(uName)))) {
1397                to.setSignalA1Name("");
1398            }
1399            if ((to.getSignalA2Name().equals(sName) || ((uName != null)
1400                    && to.getSignalA2Name().equals(uName)))) {
1401                to.setSignalA2Name("");
1402            }
1403            if ((to.getSignalA3Name().equals(sName) || ((uName != null)
1404                    && to.getSignalA3Name().equals(uName)))) {
1405                to.setSignalA3Name("");
1406            }
1407            if ((to.getSignalB1Name().equals(sName) || ((uName != null)
1408                    && to.getSignalB1Name().equals(uName)))) {
1409                to.setSignalB1Name("");
1410            }
1411            if ((to.getSignalB2Name().equals(sName) || ((uName != null)
1412                    && to.getSignalB2Name().equals(uName)))) {
1413                to.setSignalB2Name("");
1414            }
1415            if ((to.getSignalC1Name().equals(sName) || ((uName != null)
1416                    && to.getSignalC1Name().equals(uName)))) {
1417                to.setSignalC1Name("");
1418            }
1419            if ((to.getSignalC2Name().equals(sName) || ((uName != null)
1420                    && to.getSignalC2Name().equals(uName)))) {
1421                to.setSignalC2Name("");
1422            }
1423            if ((to.getSignalD1Name().equals(sName) || ((uName != null)
1424                    && to.getSignalD1Name().equals(uName)))) {
1425                to.setSignalD1Name("");
1426            }
1427            if ((to.getSignalD2Name().equals(sName) || ((uName != null)
1428                    && to.getSignalD2Name().equals(uName)))) {
1429                to.setSignalD2Name("");
1430            }
1431        }
1432        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
1433            if (po.getEastBoundSignal().equals(sName) || po.getEastBoundSignal().equals(uName)) {
1434                po.setEastBoundSignal("");
1435            }
1436            if (po.getWestBoundSignal().equals(sName) || po.getWestBoundSignal().equals(uName)) {
1437                po.setWestBoundSignal("");
1438            }
1439        }
1440        for (LevelXing x : layoutEditor.getLevelXings()) {
1441            if ( (x.getSignalAName().equals(sName) || ((uName != null)
1442                    && (x.getSignalAName().equals(uName))))) {
1443                x.setSignalAName("");
1444            }
1445            if ( (x.getSignalBName().equals(sName) || ((uName != null)
1446                    && (x.getSignalBName().equals(uName))))) {
1447                x.setSignalBName("");
1448            }
1449            if ( (x.getSignalCName().equals(sName) || ((uName != null)
1450                    && (x.getSignalCName().equals(uName))))) {
1451                x.setSignalCName("");
1452            }
1453            if ( (x.getSignalDName().equals(sName) || ((uName != null)
1454                    && (x.getSignalDName().equals(uName))))) {
1455                x.setSignalDName("");
1456            }
1457        }
1458    }   //removeAssignment
1459
1460    /**
1461     * Removes the SignalHead with the specified name from the panel and from
1462     * assignment to any turnout, positionable point, or level crossing.
1463     * @param signalName name of signal head to be removed.
1464     */
1465    public void removeSignalHeadFromPanel(@CheckForNull String signalName) {
1466        if ((signalName == null) || signalName.isEmpty()) {
1467            return;
1468        }
1469        SignalHead head = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1470        if (head != null) {
1471            removeAssignment(head);
1472            layoutEditor.removeSignalHead(head);
1473        }
1474    }
1475
1476    /*
1477    * Initializes a BlockBossLogic for creation of a signal logic for the signal
1478    * head named in "signalHeadName".
1479    * Should not be called until enough informmation has been gathered to allow
1480    * configuration of the Simple Signal Logic.
1481     */
1482    public boolean initializeBlockBossLogic(@Nonnull String signalHeadName) {
1483        logic = BlockBossLogic.getStoppedObject(signalHeadName);
1484        return true;
1485    }
1486
1487    /*
1488    * Finalizes a successfully created signal logic
1489     */
1490    public void finalizeBlockBossLogic() {
1491        if (logic == null) {
1492            return;
1493        }
1494        InstanceManager.getDefault(BlockBossLogicProvider.class).register(logic);
1495        logic.start();
1496        logic = null;
1497    }
1498
1499    /*
1500     * Returns the signal head at the end of the block "track" is assigned to.
1501     *  "track" is the Track Segment leaving "object".
1502     *  "object" must be either an anchor point or one of the connecting
1503     *  points of a turnout or level crossing.
1504     * Note: returns 'null' is signal is not present where it is expected, or
1505     *  if an End Bumper is reached. To test for end bumper, use the
1506     *  associated routine "reachedEndBumper()". Reaching a turntable ray
1507     *  track connection is considered reaching an end bumper.
1508     * Note: Normally this routine requires a signal at any turnout it finds.
1509     *  However, if 'skipIncludedTurnout' is true, this routine will skip
1510     *  over an absent signal at an included turnout, that is a turnout
1511     *  with its throat track segment and its continuing track segment in
1512     *  the same block. When this happens, the user is warned.
1513     */
1514    @CheckReturnValue
1515    public SignalHead getNextSignalFromObject(@Nonnull TrackSegment track,
1516            @Nonnull Object object,
1517            @Nonnull String signalHeadName, @Nonnull JmriJFrame frame) {
1518        hitEndBumper = false;
1519        auxSignal = null;
1520        TrackSegment t = track;
1521        Object obj = object;
1522        boolean inBlock = true;
1523        HitPointType type; // = LayoutEditor.HitPointType.NONE;
1524        Object connect = null;
1525        while (inBlock) {
1526            if (t.getConnect1() == obj) {
1527                type = t.getType2();
1528                connect = t.getConnect2();
1529            } else if (t.getConnect2() == obj) {
1530                type = t.getType1();
1531                connect = t.getConnect1();
1532            } else {
1533                log.error("Error obj not connected to {}!", t.getName());
1534                return null;
1535            }
1536            if (type == HitPointType.POS_POINT) {
1537                PositionablePoint p = (PositionablePoint) connect;
1538                if (p.getType() == PositionablePoint.PointType.END_BUMPER) {
1539                    hitEndBumper = true;
1540                    return null;
1541                }
1542                if (p.getConnect1() == t) {
1543                    t = p.getConnect2();
1544                } else {
1545                    t = p.getConnect1();
1546                }
1547                if (t == null) {
1548                    return null;
1549                }
1550                if (track.getLayoutBlock() != t.getLayoutBlock()) {
1551                    //p is a block boundary - should be signalled
1552                    String signalName;
1553                    if (isAtWestEndOfAnchor(t, p)) {
1554                        signalName = p.getWestBoundSignal();
1555                    } else {
1556                        signalName = p.getEastBoundSignal();
1557                    }
1558                    if (signalName.isEmpty()) {
1559                        return null;
1560                    }
1561                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1562                }
1563                obj = p;
1564            } else if (type == HitPointType.TURNOUT_A) {
1565                //Reached turnout throat, should be signalled
1566                LayoutTurnout to = (LayoutTurnout) connect;
1567                String signalName = to.getSignalA2Name();
1568                if (!signalName.isEmpty()) {
1569                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1570                }
1571                signalName = to.getSignalA1Name();
1572                if (signalName.isEmpty()) {
1573                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1574                        return null;
1575                    }
1576                    t = getContinuingTrack(to, type);
1577                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1578                        return null;
1579                    }
1580                    warnOfSkippedTurnout(frame, to.getTurnoutName(), signalHeadName);
1581                    obj = to;
1582                } else {
1583                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1584                }
1585            } else if (type == HitPointType.TURNOUT_B) {
1586                //Reached turnout continuing, should be signalled
1587                LayoutTurnout to = (LayoutTurnout) connect;
1588                String signalName = to.getSignalB2Name();
1589                if (to.getContinuingSense() == Turnout.THROWN) {
1590                    signalName = to.getSignalC2Name();
1591                }
1592                if (!signalName.isEmpty()) {
1593                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1594                }
1595                if (to.getContinuingSense() == Turnout.CLOSED) {
1596                    signalName = to.getSignalB1Name();
1597                } else {
1598                    signalName = to.getSignalC1Name();
1599                }
1600                if (signalName.isEmpty()) {
1601                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1602                        return null;
1603                    }
1604                    t = getContinuingTrack(to, type);
1605                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1606                        return null;
1607                    }
1608                    warnOfSkippedTurnout(frame, to.getTurnoutName(), signalHeadName);
1609                    obj = to;
1610                } else {
1611                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1612                }
1613            } else if (type == HitPointType.TURNOUT_C) {
1614                //Reached turnout diverging, should be signalled
1615                LayoutTurnout to = (LayoutTurnout) connect;
1616                String signalName = to.getSignalC2Name();
1617                if (to.getContinuingSense() == Turnout.THROWN) {
1618                    signalName = to.getSignalB2Name();
1619                }
1620                if (!signalName.isEmpty()) {
1621                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1622                }
1623                if (to.getContinuingSense() == Turnout.CLOSED) {
1624                    signalName = to.getSignalC1Name();
1625                } else {
1626                    signalName = to.getSignalB1Name();
1627                }
1628                if (signalName.isEmpty()) {
1629                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1630                        return null;
1631                    }
1632                    t = getContinuingTrack(to, type);
1633                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1634                        return null;
1635                    }
1636                    warnOfSkippedTurnout(frame, to.getTurnoutName(), signalHeadName);
1637                    obj = to;
1638                } else {
1639                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1640                }
1641            } else if (type == HitPointType.TURNOUT_D) {
1642                //Reached turnout xover 4, should be signalled
1643                LayoutTurnout to = (LayoutTurnout) connect;
1644                String signalName = to.getSignalD2Name();
1645                if (!signalName.isEmpty()) {
1646                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1647                }
1648                signalName = to.getSignalD1Name();
1649                if (signalName.isEmpty()) {
1650                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1651                        return null;
1652                    }
1653                    t = getContinuingTrack(to, type);
1654                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1655                        return null;
1656                    }
1657                    warnOfSkippedTurnout(frame, to.getTurnoutName(), signalHeadName);
1658                    obj = to;
1659                } else {
1660                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1661                }
1662            } else if (type == HitPointType.LEVEL_XING_A) {
1663                //Reached level crossing that may or may not be a block boundary
1664                LevelXing x = (LevelXing) connect;
1665                String signalName = x.getSignalAName();
1666                if ( !signalName.isEmpty() ) {
1667                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1668                }
1669                t = (TrackSegment) x.getConnectC();
1670                if (t == null) {
1671                    return null;
1672                }
1673                if (track.getLayoutBlock() != t.getLayoutBlock()) {
1674                    return null;
1675                }
1676                obj = x;
1677            } else if (type == HitPointType.LEVEL_XING_B) {
1678                //Reached level crossing that may or may not be a block boundary
1679                LevelXing x = (LevelXing) connect;
1680                String signalName = x.getSignalBName();
1681                if ( !signalName.isEmpty() ) {
1682                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1683                }
1684                t = (TrackSegment) x.getConnectD();
1685                if (t == null) {
1686                    return null;
1687                }
1688                if (track.getLayoutBlock() != t.getLayoutBlock()) {
1689                    return null;
1690                }
1691                obj = x;
1692            } else if (type == HitPointType.LEVEL_XING_C) {
1693                //Reached level crossing that may or may not be a block boundary
1694                LevelXing x = (LevelXing) connect;
1695                String signalName = x.getSignalCName();
1696                if ( !signalName.isEmpty() ) {
1697                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1698                }
1699                t = (TrackSegment) x.getConnectA();
1700                if (t == null) {
1701                    return null;
1702                }
1703                if (track.getLayoutBlock() != t.getLayoutBlock()) {
1704                    return null;
1705                }
1706                obj = x;
1707            } else if (type == HitPointType.LEVEL_XING_D) {
1708                //Reached level crossing that may or may not be a block boundary
1709                LevelXing x = (LevelXing) connect;
1710                String signalName = x.getSignalDName();
1711                if ( !signalName.isEmpty() ) {
1712                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1713                }
1714                t = (TrackSegment) x.getConnectB();
1715                if (t == null) {
1716                    return null;
1717                }
1718                if (track.getLayoutBlock() != t.getLayoutBlock()) {
1719                    return null;
1720                }
1721                obj = x;
1722            } else if (type == HitPointType.SLIP_A) {
1723                LayoutSlip sl = (LayoutSlip) connect;
1724                String signalName = sl.getSignalA2Name();
1725                if (!signalName.isEmpty()) {
1726                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1727                }
1728                signalName = sl.getSignalA1Name();
1729                if (signalName.isEmpty()) {
1730                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1731                        return null;
1732                    }
1733                    t = getContinuingTrack(sl, type);
1734                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1735                        return null;
1736                    }
1737                    warnOfSkippedTurnout(frame, sl.getTurnoutName(), signalHeadName);
1738                    obj = sl;
1739                } else {
1740                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1741                }
1742            } else if (type == HitPointType.SLIP_B) {
1743                LayoutSlip sl = (LayoutSlip) connect;
1744                String signalName;
1745                if (sl.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) {
1746                    signalName = sl.getSignalB2Name();
1747                    if (!signalName.isEmpty()) {
1748                        auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1749                    }
1750                }
1751                signalName = sl.getSignalB1Name();
1752                if (signalName.isEmpty()) {
1753                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1754                        return null;
1755                    }
1756                    t = getContinuingTrack(sl, type);
1757                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1758                        return null;
1759                    }
1760                    warnOfSkippedTurnout(frame, sl.getTurnoutName(), signalHeadName);
1761                    obj = sl;
1762                } else {
1763                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1764                }
1765            } else if (type == HitPointType.SLIP_C) {
1766                LayoutSlip sl = (LayoutSlip) connect;
1767                String signalName;
1768                if (sl.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) {
1769                    signalName = sl.getSignalC2Name();
1770                    if (!signalName.isEmpty()) {
1771                        auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1772                    }
1773                }
1774                signalName = sl.getSignalC1Name();
1775                if (signalName.isEmpty()) {
1776                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1777                        return null;
1778                    }
1779                    t = getContinuingTrack(sl, type);
1780                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1781                        return null;
1782                    }
1783                    warnOfSkippedTurnout(frame, sl.getTurnoutName(), signalHeadName);
1784                    obj = sl;
1785                } else {
1786                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1787                }
1788            } else if (type == HitPointType.SLIP_D) {
1789                LayoutSlip sl = (LayoutSlip) connect;
1790                String signalName = sl.getSignalD2Name();
1791                if (!signalName.isEmpty()) {
1792                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1793                }
1794                signalName = sl.getSignalD1Name();
1795                if (signalName.isEmpty()) {
1796                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1797                        return null;
1798                    }
1799                    t = getContinuingTrack(sl, type);
1800                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1801                        return null;
1802                    }
1803                    warnOfSkippedTurnout(frame, sl.getTurnoutName(), signalHeadName);
1804                    obj = sl;
1805                } else {
1806                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1807                }
1808            } else if (HitPointType.isTurntableRayHitType(type)) {
1809                hitEndBumper = true;
1810                return null;
1811            }
1812        }
1813        return null;
1814    }   //getNextSignalFromObject
1815
1816    private boolean hitEndBumper = false;
1817
1818    private void warnOfSkippedTurnout(
1819            @Nonnull JFrame frame,
1820            @Nonnull String turnoutName,
1821            @Nonnull String signalHeadName) {
1822        JOptionPane.showMessageDialog(frame,
1823                Bundle.getMessage("SignalsWarn2",
1824                        new Object[]{turnoutName, signalHeadName}),
1825                Bundle.getMessage("WarningTitle"),
1826                JOptionPane.WARNING_MESSAGE);
1827    }
1828
1829    @CheckReturnValue
1830    private TrackSegment getContinuingTrack(@Nonnull LayoutTurnout to, HitPointType type) {
1831        LayoutTurnout.TurnoutType ty = to.getTurnoutType();
1832        if ((ty == LayoutTurnout.TurnoutType.RH_TURNOUT) || (ty == LayoutTurnout.TurnoutType.LH_TURNOUT)) {
1833            if (type == HitPointType.TURNOUT_A) {
1834                if (to.getContinuingSense() == Turnout.CLOSED) {
1835                    return (TrackSegment) to.getConnectB();
1836                } else {
1837                    return (TrackSegment) to.getConnectC();
1838                }
1839            } else {
1840                return (TrackSegment) to.getConnectA();
1841            }
1842        } else if ((ty == LayoutTurnout.TurnoutType.DOUBLE_XOVER) || (ty == LayoutTurnout.TurnoutType.RH_XOVER)
1843                || (ty == LayoutTurnout.TurnoutType.LH_XOVER)) {
1844            if (type == HitPointType.TURNOUT_A) {
1845                return (TrackSegment) to.getConnectB();
1846            } else if (type == HitPointType.TURNOUT_B) {
1847                return (TrackSegment) to.getConnectA();
1848            } else if (type == HitPointType.TURNOUT_C) {
1849                return (TrackSegment) to.getConnectD();
1850            } else if (type == HitPointType.TURNOUT_D) {
1851                return (TrackSegment) to.getConnectC();
1852            }
1853        }
1854        log.error("Bad connection type around turnout {}", to.getTurnoutName());
1855        return null;
1856    }
1857
1858    /*
1859     * Returns 'true' if an end bumper was reached during the last call to
1860     *  GetNextSignalFromObject. Also used in the odd case of reaching a
1861     *  turntable ray track connection, which is treated as an end
1862     *  bumper here.
1863     */
1864    public boolean reachedEndBumper() {
1865        return hitEndBumper;
1866    }
1867
1868    /*
1869     * Returns 'true' if "track" enters a block boundary at the west(north) end of
1870     *  "point". Returns "false" otherwise. If track is neither horizontal or
1871     *  vertical, assumes horizontal, as done when setting signals at block boundary.
1872     *  "track" is a TrackSegment connected to "point".
1873     *  "point" is an anchor point serving as a block boundary.
1874     * <p>
1875     * This is the member function method, which is preferred. See the static
1876     * method following.
1877     */
1878    public boolean isAtWestEndOfAnchor(TrackSegment t, PositionablePoint p) {
1879        return LayoutEditorTools.isAtWestEndOfAnchor(layoutEditor, t, p); // invoke status locally
1880    }
1881
1882
1883    /*
1884     * Returns 'true' if "track" enters a block boundary at the west(north) end of
1885     *  "point". Returns "false" otherwise. If track is neither horizontal or
1886     *  vertical, assumes horizontal, as done when setting signals at block boundary.
1887     *  "track" is a TrackSegment connected to "point".
1888     *  "point" is an anchor point serving as a block boundary.
1889     * <p>
1890     * This is the static form, which requires context information from
1891     * a passed LayoutEditor reference; the member function is preferred because
1892     * some day we want to get rid of the LayoutEditor combined pseudo-global and panel reference.
1893     */
1894    public static boolean isAtWestEndOfAnchor(LayoutEditor layoutEditor, TrackSegment t, PositionablePoint p) {
1895        if (p.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
1896            if (p.getConnect1() == t) {
1897                if (p.getConnect1Dir() == Path.NORTH || p.getConnect1Dir() == Path.WEST) {
1898                    return false;
1899                }
1900                return true;
1901            } else {
1902                if (p.getConnect1Dir() == Path.NORTH || p.getConnect1Dir() == Path.WEST) {
1903                    return true;
1904                }
1905                return false;
1906            }
1907
1908        }
1909        TrackSegment tx = null;
1910        if (p.getConnect1() == t) {
1911            tx = p.getConnect2();
1912        } else if (p.getConnect2() == t) {
1913            tx = p.getConnect1();
1914        } else {
1915            log.error("track not connected to anchor point");
1916            return false;
1917        }
1918
1919        Point2D coords1;
1920        if (t.getConnect1() == p) {
1921            coords1 = layoutEditor.getCoords(t.getConnect2(), t.getType2());
1922        } else {
1923            coords1 = layoutEditor.getCoords(t.getConnect1(), t.getType1());
1924        }
1925
1926        Point2D coords2;
1927        if (tx != null) {
1928            if (tx.getConnect1() == p) {
1929                coords2 = layoutEditor.getCoords(tx.getConnect2(), tx.getType2());
1930            } else {
1931                coords2 = layoutEditor.getCoords(tx.getConnect1(), tx.getType1());
1932            }
1933        } else {
1934            if (t.getConnect1() == p) {
1935                coords2 = layoutEditor.getCoords(t.getConnect1(), t.getType1());
1936            } else {
1937                coords2 = layoutEditor.getCoords(t.getConnect2(), t.getType2());
1938            }
1939        }
1940
1941        double delX = coords1.getX() - coords2.getX();
1942        double delY = coords1.getY() - coords2.getY();
1943        if (Math.abs(delX) > 2.0 * Math.abs(delY)) {
1944            //track is primarily horizontal
1945            if (delX > 0.0) {
1946                return false;
1947            } else {
1948                return true;
1949            }
1950        } else if (Math.abs(delY) > 2.0 * Math.abs(delX)) {
1951            //track is primarily vertical
1952            if (delY > 0.0) {
1953                return false;
1954            } else {
1955                return true;
1956            }
1957        }
1958        // track is not primarily horizontal or vertical; assume horizontal
1959        // log.error ("Track is not vertical or horizontal at anchor");
1960        if (delX > 0.0) {
1961            return false;
1962        }
1963        return true;
1964    }
1965
1966    /*===========================*\
1967    |* setSignalsAtBlockBoundary *|
1968    \*===========================*/
1969    /**
1970     * Tool to set signals at a block boundary, including placing the signal
1971     * icons and setup of Simple Signal Logic for each signal head
1972     * <p>
1973     * Block boundary must be at an Anchor Point on the LayoutEditor panel.
1974     */
1975    //operational variables for Set Signals at Block Boundary tool
1976    private JmriJFrame setSignalsAtBlockBoundaryFrame = null;
1977    private boolean setSignalsAtBlockBoundaryOpenFlag = false;
1978    private boolean setSignalsAtBlockBoundaryFromMenuFlag = false;
1979
1980    private JLabel block1NameLabel = null;
1981    private JLabel block2NameLabel = null;
1982
1983    private final NamedBeanComboBox<Block> block1IDComboBox = new NamedBeanComboBox<>(
1984            InstanceManager.getDefault(BlockManager.class),
1985            null, DisplayOptions.DISPLAYNAME);
1986    private final NamedBeanComboBox<Block> block2IDComboBox = new NamedBeanComboBox<>(
1987            InstanceManager.getDefault(BlockManager.class),
1988            null, DisplayOptions.DISPLAYNAME);
1989
1990    private final NamedBeanComboBox<SignalHead> eastBoundSignalHeadComboBox = new NamedBeanComboBox<>(
1991            InstanceManager.getDefault(SignalHeadManager.class),
1992            null, DisplayOptions.DISPLAYNAME);
1993    private final NamedBeanComboBox<SignalHead> westBoundSignalHeadComboBox = new NamedBeanComboBox<>(
1994            InstanceManager.getDefault(SignalHeadManager.class),
1995            null, DisplayOptions.DISPLAYNAME);
1996
1997    private final JCheckBox setEastBound = new JCheckBox(Bundle.getMessage("PlaceHead"));
1998    private final JCheckBox setupLogicEastBound = new JCheckBox(Bundle.getMessage("SetLogic"));
1999    private final JCheckBox setWestBound = new JCheckBox(Bundle.getMessage("PlaceHead"));
2000    private final JCheckBox setupLogicWestBound = new JCheckBox(Bundle.getMessage("SetLogic"));
2001
2002    private JButton getAnchorSavedSignalHeads = null;
2003    private JButton changeSignalAtBoundaryIcon = null;
2004    private JButton setSignalsAtBlockBoundaryDone = null;
2005    private JButton setSignalsAtBlockBoundaryCancel = null;
2006
2007    private LayoutBlock block1 = null;
2008    private LayoutBlock block2 = null;
2009
2010    private TrackSegment eastTrack = null;
2011    private TrackSegment westTrack = null;
2012
2013    private PositionablePoint boundary = null;
2014    private SignalHead eastBoundHead = null;
2015    private SignalHead westBoundHead = null;
2016
2017    private boolean showWest = true;
2018    private boolean showEast = true;
2019
2020    //display dialog for Set Signals at Block Boundary tool
2021    public void setSignalsAtBlockBoundaryFromMenu(PositionablePoint p,
2022            MultiIconEditor theEditor,
2023            JFrame theFrame) {
2024        boundary = p;
2025
2026        //if this is an edge connector...
2027        if ((p.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) && ((p.getLinkedPoint() == null)
2028                || (p.getLinkedPoint().getConnect1() == null))) {
2029            if (p.getConnect1Dir() == Path.EAST || p.getConnect1Dir() == Path.SOUTH) {
2030                showWest = false;
2031            } else {
2032                showEast = false;
2033            }
2034            block1IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
2035        } else {
2036            block1IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
2037            block2IDComboBox.setSelectedItem(boundary.getConnect2().getLayoutBlock().getBlock());
2038        }
2039        setSignalsAtBlockBoundaryFromMenuFlag = true;
2040        setSignalsAtBlockBoundary(theEditor, theFrame);
2041        setSignalsAtBlockBoundaryFromMenuFlag = false;
2042    }
2043
2044    public void setSignalsAtBlockBoundary(MultiIconEditor theEditor, JFrame theFrame) {
2045        signalIconEditor = theEditor;
2046        signalFrame = theFrame;
2047
2048        //Initialize if needed
2049        if (setSignalsAtBlockBoundaryFrame == null) {
2050            setSignalsAtBlockBoundaryOpenFlag = false;
2051            setSignalsAtBlockBoundaryFrame = new JmriJFrame(Bundle.getMessage("SignalsAtBoundary"), false, true);
2052            oneFrameToRuleThemAll(setSignalsAtBlockBoundaryFrame);
2053            setSignalsAtBlockBoundaryFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
2054            setSignalsAtBlockBoundaryFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtBoundary", true);
2055            setSignalsAtBlockBoundaryFrame.setLocation(70, 30);
2056            Container theContentPane = setSignalsAtBlockBoundaryFrame.getContentPane();
2057            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
2058
2059            JPanel panel11 = new JPanel(new FlowLayout());
2060            block1NameLabel = new JLabel(
2061                    Bundle.getMessage("MakeLabel",
2062                            Bundle.getMessage("BeanNameBlock") + " 1 "));
2063            panel11.add(block1NameLabel);
2064            panel11.add(block1IDComboBox);
2065            block1IDComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
2066            theContentPane.add(panel11);
2067
2068            JPanel panel12 = new JPanel(new FlowLayout());
2069            block2NameLabel = new JLabel(
2070                    Bundle.getMessage("MakeLabel",
2071                            Bundle.getMessage("BeanNameBlock")
2072                            + " 2 " + Bundle.getMessage("Name")));
2073            panel12.add(block2NameLabel);
2074            panel12.add(block2IDComboBox);
2075            block2IDComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
2076            theContentPane.add(panel12);
2077            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
2078
2079            JPanel panel2 = new JPanel(new FlowLayout());
2080            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
2081            panel2.add(shTitle);
2082            panel2.add(new JLabel("   "));
2083            panel2.add(getAnchorSavedSignalHeads = new JButton(Bundle.getMessage("GetSaved")));
2084            getAnchorSavedSignalHeads.addActionListener(this::getSavedAnchorSignals);
2085            getAnchorSavedSignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
2086            theContentPane.add(panel2);
2087            if (showEast) {
2088                JPanel panel21 = new JPanel(new FlowLayout());
2089                JLabel eastBoundLabel = new JLabel(Bundle.getMessage("East/SouthBound") + ": ");
2090                panel21.add(eastBoundLabel);
2091                panel21.add(eastBoundSignalHeadComboBox);
2092                theContentPane.add(panel21);
2093                eastBoundSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadEastNameHint"));
2094
2095                JPanel panel22 = new JPanel(new FlowLayout());
2096                panel22.add(new JLabel("   "));
2097                panel22.add(setEastBound);
2098                setEastBound.setToolTipText(Bundle.getMessage("AnchorPlaceHeadHint"));
2099                panel22.add(new JLabel("  "));
2100                if (showWest) {
2101                    panel22.add(setupLogicEastBound);
2102                    setupLogicEastBound.setToolTipText(Bundle.getMessage("SetLogicHint"));
2103                }
2104                theContentPane.add(panel22);
2105            }
2106            if (showWest) {
2107                JPanel panel31 = new JPanel(new FlowLayout());
2108                JLabel westBoundLabel = new JLabel(Bundle.getMessage("West/NorthBound") + ": ");
2109                panel31.add(westBoundLabel);
2110                panel31.add(westBoundSignalHeadComboBox);
2111                theContentPane.add(panel31);
2112                westBoundSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadWestNameHint"));
2113
2114                JPanel panel32 = new JPanel(new FlowLayout());
2115                panel32.add(new JLabel("   "));
2116                panel32.add(setWestBound);
2117                setWestBound.setToolTipText(Bundle.getMessage("AnchorPlaceHeadHint"));
2118                panel32.add(new JLabel("  "));
2119                if (showEast) {
2120                    panel32.add(setupLogicWestBound);
2121                    setupLogicWestBound.setToolTipText(Bundle.getMessage("SetLogicHint"));
2122                }
2123                theContentPane.add(panel32);
2124            }
2125
2126            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
2127
2128            JPanel panel6 = new JPanel(new FlowLayout());
2129            panel6.add(changeSignalAtBoundaryIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
2130            changeSignalAtBoundaryIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
2131            changeSignalAtBoundaryIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
2132            panel6.add(new JLabel("   "));
2133            panel6.add(setSignalsAtBlockBoundaryDone = new JButton(Bundle.getMessage("ButtonDone")));
2134            setSignalsAtBlockBoundaryDone.addActionListener(this::setSignalsAtBlockBoundaryDonePressed);
2135            setSignalsAtBlockBoundaryDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
2136
2137            panel6.add(setSignalsAtBlockBoundaryCancel = new JButton(Bundle.getMessage("ButtonCancel")));
2138            setSignalsAtBlockBoundaryCancel.addActionListener(this::setSignalsAtBlockBoundaryCancelPressed);
2139            setSignalsAtBlockBoundaryCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
2140            theContentPane.add(panel6);
2141
2142            //make this button the default button (return or enter activates)
2143            JRootPane rootPane = SwingUtilities.getRootPane(setSignalsAtBlockBoundaryDone);
2144            if (rootPane != null) { //this should never happen but just in case...
2145                rootPane.setDefaultButton(setSignalsAtBlockBoundaryDone);
2146            }
2147
2148            setSignalsAtBlockBoundaryFrame.addWindowListener(new WindowAdapter() {
2149                @Override
2150                public void windowClosing(WindowEvent e) {
2151                    setSignalsAtBlockBoundaryCancelPressed(null);
2152                }
2153            });
2154        }
2155
2156        block1IDComboBox.setVisible(!setSignalsAtBlockBoundaryFromMenuFlag);
2157        block2IDComboBox.setVisible(!setSignalsAtBlockBoundaryFromMenuFlag);
2158
2159        if (setSignalsAtBlockBoundaryFromMenuFlag) {
2160            getSavedAnchorSignals(null);
2161            block1NameLabel.setText(Bundle.getMessage("MakeLabel",
2162                    Bundle.getMessage("BeanNameBlock")
2163                    + " 1 " + Bundle.getMessage("Name"))
2164                    + boundary.getConnect1().getLayoutBlock().getId());
2165            if (boundary.getConnect2() != null) {
2166                block2NameLabel.setText(Bundle.getMessage("MakeLabel",
2167                        Bundle.getMessage("BeanNameBlock")
2168                        + " 2 " + Bundle.getMessage("Name"))
2169                        + boundary.getConnect2().getLayoutBlock().getId());
2170            }
2171        }
2172
2173        if (!setSignalsAtBlockBoundaryOpenFlag) {
2174            setSignalsAtBlockBoundaryFrame.setPreferredSize(null);
2175            setSignalsAtBlockBoundaryFrame.pack();
2176            setSignalsAtBlockBoundaryOpenFlag = true;
2177        }
2178        setSignalsAtBlockBoundaryFrame.setVisible(true);
2179    }   //setSignalsAtBlockBoundary
2180
2181    private void getSavedAnchorSignals(ActionEvent a) {
2182        if (!getBlockInformation()) {
2183            return;
2184        }
2185        eastBoundSignalHeadComboBox.setSelectedItem(boundary.getEastBoundSignalHead());
2186        westBoundSignalHeadComboBox.setSelectedItem(boundary.getWestBoundSignalHead());
2187    }
2188
2189    private void setSignalsAtBlockBoundaryCancelPressed(ActionEvent a) {
2190        setSignalsAtBlockBoundaryOpenFlag = false;
2191        setSignalsAtBlockBoundaryFrame.setVisible(false);
2192    }
2193
2194    private void setSignalsAtBlockBoundaryDonePressed(ActionEvent a) {
2195        if (!getBlockInformation()) {
2196            return;
2197        }
2198        eastBoundHead = getSignalHeadFromEntry(eastBoundSignalHeadComboBox, false, setSignalsAtBlockBoundaryFrame);
2199        westBoundHead = getSignalHeadFromEntry(westBoundSignalHeadComboBox, false, setSignalsAtBlockBoundaryFrame);
2200        if ((eastBoundHead == null) && (westBoundHead == null)) {
2201            JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2202                    Bundle.getMessage("SignalsError12"),
2203                    Bundle.getMessage("ErrorTitle"),
2204                    JOptionPane.ERROR_MESSAGE);
2205            return;
2206        }
2207        //place or update signals as requested
2208        String newEastBoundSignalName = eastBoundSignalHeadComboBox.getSelectedItemDisplayName();
2209        if (newEastBoundSignalName == null) {
2210            newEastBoundSignalName = "";
2211        }
2212        if ((eastBoundHead != null) && setEastBound.isSelected()) {
2213            if (isHeadOnPanel(eastBoundHead)
2214                    && (eastBoundHead != getHeadFromName(boundary.getEastBoundSignal()))) {
2215                JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2216                        Bundle.getMessage("SignalsError6",
2217                                new Object[]{newEastBoundSignalName}),
2218                        Bundle.getMessage("ErrorTitle"),
2219                        JOptionPane.ERROR_MESSAGE);
2220                return;
2221            } else {
2222                removeSignalHeadFromPanel(boundary.getEastBoundSignal());
2223                placeEastBound();
2224                removeAssignment(eastBoundHead);
2225                boundary.setEastBoundSignal(newEastBoundSignalName);
2226                needRedraw = true;
2227            }
2228        } else if ((eastBoundHead != null)
2229                && (eastBoundHead != getHeadFromName(boundary.getEastBoundSignal()))
2230                && (eastBoundHead != getHeadFromName(boundary.getWestBoundSignal()))) {
2231            if (isHeadOnPanel(eastBoundHead)) {
2232                JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2233                        Bundle.getMessage("SignalsError13",
2234                                new Object[]{newEastBoundSignalName}),
2235                        Bundle.getMessage("ErrorTitle"),
2236                        JOptionPane.ERROR_MESSAGE);
2237                return;
2238            } else {
2239                removeSignalHeadFromPanel(boundary.getEastBoundSignal());
2240                removeAssignment(eastBoundHead);
2241                boundary.setEastBoundSignal(newEastBoundSignalName);
2242            }
2243            //} else if ((eastBoundHead != null)
2244            //            && (eastBoundHead == getHeadFromName(boundary.getWestBoundSignal()))) {
2245            //need to figure out what to do in this case.
2246        }
2247        String newWestBoundSignalName = westBoundSignalHeadComboBox.getSelectedItemDisplayName();
2248        if (newWestBoundSignalName == null) {
2249            newWestBoundSignalName = "";
2250        }
2251        if ((westBoundHead != null) && setWestBound.isSelected()) {
2252            if (isHeadOnPanel(westBoundHead)
2253                    && (westBoundHead != getHeadFromName(boundary.getWestBoundSignal()))) {
2254                JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2255                        Bundle.getMessage("SignalsError6",
2256                                new Object[]{newWestBoundSignalName}),
2257                        Bundle.getMessage("ErrorTitle"),
2258                        JOptionPane.ERROR_MESSAGE);
2259                return;
2260            } else {
2261                removeSignalHeadFromPanel(boundary.getWestBoundSignal());
2262                placeWestBound();
2263                removeAssignment(westBoundHead);
2264                boundary.setWestBoundSignal(newWestBoundSignalName);
2265                needRedraw = true;
2266            }
2267        } else if ((westBoundHead != null)
2268                && (westBoundHead != getHeadFromName(boundary.getEastBoundSignal()))
2269                && (westBoundHead != getHeadFromName(boundary.getWestBoundSignal()))) {
2270            if (isHeadOnPanel(westBoundHead)) {
2271                JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2272                        Bundle.getMessage("SignalsError13",
2273                                new Object[]{newWestBoundSignalName}),
2274                        Bundle.getMessage("ErrorTitle"),
2275                        JOptionPane.ERROR_MESSAGE);
2276                return;
2277            } else {
2278                removeSignalHeadFromPanel(boundary.getWestBoundSignal());
2279                removeAssignment(westBoundHead);
2280                boundary.setWestBoundSignal(newWestBoundSignalName);
2281            }
2282            //} else if ((westBoundHead != null)
2283            //    && (westBoundHead == getHeadFromName(boundary.getEastBoundSignal()))) {
2284            //need to figure out what to do in this case.
2285        }
2286        if ((eastBoundHead != null) && setupLogicEastBound.isSelected()) {
2287            setLogicEastBound();
2288        }
2289        if ((westBoundHead != null) && setupLogicWestBound.isSelected()) {
2290            setLogicWestBound();
2291        }
2292        setSignalsAtBlockBoundaryOpenFlag = false;
2293        setSignalsAtBlockBoundaryFrame.setVisible(false);
2294        if (needRedraw) {
2295            layoutEditor.redrawPanel();
2296            needRedraw = false;
2297            layoutEditor.setDirty();
2298        }
2299    }   //setSignalsAtBlockBoundaryDonePressed
2300
2301    /*
2302    * Do some thing here for end bumpers.
2303     */
2304    private boolean getBlockInformation() {
2305        //might have to do something to trick it with an end bumper
2306        if (!setSignalsAtBlockBoundaryFromMenuFlag) {
2307            block1 = getBlockFromEntry(block1IDComboBox);
2308            if (block1 == null) {
2309                return false;
2310            }
2311            block2 = getBlockFromEntry(block2IDComboBox);
2312            if (block2 == null) {
2313                return false;
2314            }
2315            boundary = null;
2316            for (PositionablePoint p : layoutEditor.getPositionablePoints()) {
2317                if (p.getType() == PositionablePoint.PointType.ANCHOR || p.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
2318                    LayoutBlock bA = null;
2319                    LayoutBlock bB = null;
2320                    if (p.getConnect1() != null) {
2321                        bA = p.getConnect1().getLayoutBlock();
2322                    }
2323                    if (p.getConnect2() != null) {
2324                        bB = p.getConnect2().getLayoutBlock();
2325                    }
2326                    if ((bA != null) && (bB != null) && (bA != bB)) {
2327                        if (((bA == block1) && (bB == block2))
2328                                || ((bA == block2) && (bB == block1))) {
2329                            boundary = p;
2330                            break;
2331                        }
2332                    }
2333                }
2334            }
2335            if (boundary == null) {
2336                JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2337                        Bundle.getMessage("SignalsError7"),
2338                        Bundle.getMessage("ErrorTitle"),
2339                        JOptionPane.ERROR_MESSAGE);
2340                return false;
2341            }
2342        }
2343        //set track orientation at boundary
2344        eastTrack = null;
2345        westTrack = null;
2346        TrackSegment track1 = boundary.getConnect1();
2347        Point2D coords1;
2348        if (track1.getConnect1() == boundary) {
2349            coords1 = layoutEditor.getCoords(track1.getConnect2(), track1.getType2());
2350        } else {
2351            coords1 = layoutEditor.getCoords(track1.getConnect1(), track1.getType1());
2352        }
2353        TrackSegment track2 = boundary.getConnect2();
2354
2355        if (boundary.getType() == PositionablePoint.PointType.END_BUMPER) {
2356            return true;
2357        }
2358        if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
2359            if (boundary.getConnect1Dir() == Path.EAST || boundary.getConnect1Dir() == Path.SOUTH) {
2360                eastTrack = track2;
2361                westTrack = track1;
2362            } else {
2363                westTrack = track2;
2364                eastTrack = track1;
2365            }
2366            return true;
2367        }
2368        Point2D coords2;
2369        if (track2.getConnect1() == boundary) {
2370            coords2 = layoutEditor.getCoords(track2.getConnect2(), track2.getType2());
2371        } else {
2372            coords2 = layoutEditor.getCoords(track2.getConnect1(), track2.getType1());
2373        }
2374
2375        placeSignalDirectionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coords2, coords1));
2376
2377        double delX = coords1.getX() - coords2.getX();
2378        double delY = coords1.getY() - coords2.getY();
2379
2380        if (Math.abs(delX) >= Math.abs(delY)) {
2381            if (delX > 0.0) {
2382                eastTrack = track1;
2383                westTrack = track2;
2384            } else {
2385                eastTrack = track2;
2386                westTrack = track1;
2387            }
2388        } else {
2389            if (delY > 0.0) {
2390                eastTrack = track1; //south
2391                westTrack = track2; //north
2392            } else {
2393                eastTrack = track2; //south
2394                westTrack = track1; //north
2395            }
2396        }
2397        return true;
2398    }   //getBlockInformation
2399
2400    @CheckReturnValue
2401    private LayoutBlock getBlockFromEntry(@Nonnull NamedBeanComboBox<Block> blockNameComboBox) {
2402        return getBlockFromEntry(blockNameComboBox.getSelectedItemDisplayName());
2403    }
2404
2405    @CheckReturnValue
2406    private LayoutBlock getBlockFromEntry(@CheckForNull String theBlockName) {
2407        if ((theBlockName == null) || theBlockName.isEmpty()) {
2408            JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2409                    Bundle.getMessage("SignalsError9"),
2410                    Bundle.getMessage("ErrorTitle"),
2411                    JOptionPane.ERROR_MESSAGE);
2412            return null;
2413        }
2414        LayoutBlock block = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(theBlockName);
2415        if (block == null) {
2416            block = InstanceManager.getDefault(LayoutBlockManager.class).getBySystemName(theBlockName);
2417            if (block == null) {
2418                JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2419                        Bundle.getMessage("SignalsError10",
2420                                new Object[]{theBlockName}), Bundle.getMessage("ErrorTitle"),
2421                        JOptionPane.ERROR_MESSAGE);
2422                return null;
2423            }
2424        }
2425        if (!block.isOnPanel(layoutEditor)
2426                && ((boundary == null) || boundary.getType() != PositionablePoint.PointType.EDGE_CONNECTOR)) {
2427            JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2428                    Bundle.getMessage("SignalsError11",
2429                            new Object[]{theBlockName}), Bundle.getMessage("ErrorTitle"),
2430                    JOptionPane.ERROR_MESSAGE);
2431            return null;
2432        }
2433        return (block);
2434    }
2435
2436    private void placeEastBound() {
2437        if (testIcon == null) {
2438            testIcon = signalIconEditor.getIcon(0);
2439        }
2440        String signalHeadName = eastBoundSignalHeadComboBox.getSelectedItemDisplayName();
2441        if (signalHeadName == null) {
2442            signalHeadName = "";
2443        }
2444        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
2445
2446        Point2D coords = layoutEditor.getLayoutTrackView(boundary).getCoordsCenter();
2447        Point2D delta = new Point2D.Double(0.0, +shift);
2448
2449        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
2450        Point2D where = MathUtil.add(coords, delta);
2451        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
2452    }
2453
2454    private void placeWestBound() {
2455        if (testIcon == null) {
2456            testIcon = signalIconEditor.getIcon(0);
2457        }
2458        String signalHeadName = westBoundSignalHeadComboBox.getSelectedItemDisplayName();
2459        if (signalHeadName == null) {
2460            signalHeadName = "";
2461        }
2462        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
2463
2464        Point2D coords = layoutEditor.getLayoutTrackView(boundary).getCoordsCenter();
2465
2466        Point2D delta = new Point2D.Double(0.0, -shift);
2467        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
2468        Point2D where = MathUtil.add(coords, delta);
2469        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
2470    }
2471
2472    private void setLogicEastBound() {
2473        LayoutBlock eastBlock = eastTrack.getLayoutBlock();
2474        Sensor eastBlockOccupancy = eastBlock.getOccupancySensor();
2475        if (eastBlockOccupancy == null) {
2476            JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2477                    Bundle.getMessage("InfoMessage4",
2478                            new Object[]{eastBlock.getUserName()}),
2479                    Bundle.getMessage("MessageTitle"),
2480                    JOptionPane.INFORMATION_MESSAGE);
2481            return;
2482        }
2483        PositionablePoint p = boundary;
2484        if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR && eastTrack != boundary.getConnect1()) {
2485            p = boundary.getLinkedPoint();
2486        }
2487        String newEastBoundSignalName = eastBoundSignalHeadComboBox.getSelectedItemDisplayName();
2488        if (newEastBoundSignalName == null) {
2489            newEastBoundSignalName = "";
2490        }
2491        SignalHead nextHead = getNextSignalFromObject(eastTrack,
2492                p, newEastBoundSignalName, setSignalsAtBlockBoundaryFrame);
2493        if ((nextHead == null) && (!reachedEndBumper())) {
2494            JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2495                    Bundle.getMessage("InfoMessage5",
2496                            new Object[]{eastBlock.getUserName()}),
2497                    Bundle.getMessage("MessageTitle"),
2498                    JOptionPane.INFORMATION_MESSAGE);
2499            return;
2500        }
2501
2502        if (!initializeBlockBossLogic(newEastBoundSignalName)) {
2503            return;
2504        }
2505        logic.setMode(BlockBossLogic.SINGLEBLOCK);
2506        logic.setSensor1(eastBlockOccupancy.getDisplayName());
2507        if (nextHead != null) {
2508            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
2509        }
2510        if (auxSignal != null) {
2511            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
2512        }
2513        finalizeBlockBossLogic();
2514    }
2515
2516    private void setLogicWestBound() {
2517        LayoutBlock westBlock = westTrack.getLayoutBlock();
2518        Sensor westBlockOccupancy = westBlock.getOccupancySensor();
2519        if (westBlockOccupancy == null) {
2520            JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2521                    Bundle.getMessage("InfoMessage4",
2522                            new Object[]{westBlock.getUserName()}),
2523                    Bundle.getMessage("MessageTitle"),
2524                    JOptionPane.INFORMATION_MESSAGE);
2525            return;
2526        }
2527        PositionablePoint p = boundary;
2528        if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR && westTrack != boundary.getConnect1()) {
2529            p = boundary.getLinkedPoint();
2530        }
2531        String newWestBoundSignalName = westBoundSignalHeadComboBox.getSelectedItemDisplayName();
2532        if (newWestBoundSignalName == null) {
2533            newWestBoundSignalName = "";
2534        }
2535        SignalHead nextHead = getNextSignalFromObject(westTrack,
2536                p, newWestBoundSignalName, setSignalsAtBlockBoundaryFrame);
2537        if ((nextHead == null) && (!reachedEndBumper())) {
2538            JOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2539                    Bundle.getMessage("InfoMessage5",
2540                            new Object[]{westBlock.getUserName()}),
2541                    Bundle.getMessage("MessageTitle"),
2542                    JOptionPane.INFORMATION_MESSAGE);
2543            return;
2544        }
2545        if (!initializeBlockBossLogic(newWestBoundSignalName)) {
2546            return;
2547        }
2548        logic.setMode(BlockBossLogic.SINGLEBLOCK);
2549        logic.setSensor1(westBlockOccupancy.getDisplayName());
2550        if (nextHead != null) {
2551            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
2552        }
2553        if (auxSignal != null) {
2554            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
2555        }
2556        finalizeBlockBossLogic();
2557    }
2558
2559    /*==========================*\
2560    |* setSignalsAtXoverTurnout *|
2561    \*==========================*/
2562    /**
2563     * Tool to set signals at a double crossover turnout, including placing the
2564     * signal icons and setup of Simple Signal Logic for each signal head
2565     * <p>
2566     * This tool assumes left facing signal head icons have been selected, and
2567     * will rotate the signal head icons accordingly.
2568     * <p>
2569     * This tool will place icons on the outside edge of the turnout.
2570     * <p>
2571     * At least one signal at each of the four connection points is required. A
2572     * second signal at each is optional.
2573     */
2574    //operational variables for Set Signals at Double Crossover Turnout tool
2575    private JmriJFrame setSignalsAtXoverTurnoutFrame = null;
2576    private boolean setSignalsAtXoverTurnoutOpenFlag = false;
2577    private boolean setSignalsAtXoverTurnoutFromMenuFlag = false;
2578
2579    private final NamedBeanComboBox<SignalHead> a1SignalHeadComboBox = new NamedBeanComboBox<>(
2580            InstanceManager.getDefault(SignalHeadManager.class),
2581            null, DisplayOptions.DISPLAYNAME);
2582    private final NamedBeanComboBox<SignalHead> a2SignalHeadComboBox = new NamedBeanComboBox<>(
2583            InstanceManager.getDefault(SignalHeadManager.class),
2584            null, DisplayOptions.DISPLAYNAME);
2585    private final NamedBeanComboBox<SignalHead> b1SignalHeadComboBox = new NamedBeanComboBox<>(
2586            InstanceManager.getDefault(SignalHeadManager.class),
2587            null, DisplayOptions.DISPLAYNAME);
2588    private final NamedBeanComboBox<SignalHead> b2SignalHeadComboBox = new NamedBeanComboBox<>(
2589            InstanceManager.getDefault(SignalHeadManager.class),
2590            null, DisplayOptions.DISPLAYNAME);
2591    private final NamedBeanComboBox<SignalHead> c1SignalHeadComboBox = new NamedBeanComboBox<>(
2592            InstanceManager.getDefault(SignalHeadManager.class),
2593            null, DisplayOptions.DISPLAYNAME);
2594    private final NamedBeanComboBox<SignalHead> c2SignalHeadComboBox = new NamedBeanComboBox<>(
2595            InstanceManager.getDefault(SignalHeadManager.class),
2596            null, DisplayOptions.DISPLAYNAME);
2597    private final NamedBeanComboBox<SignalHead> d1SignalHeadComboBox = new NamedBeanComboBox<>(
2598            InstanceManager.getDefault(SignalHeadManager.class),
2599            null, DisplayOptions.DISPLAYNAME);
2600    private final NamedBeanComboBox<SignalHead> d2SignalHeadComboBox = new NamedBeanComboBox<>(
2601            InstanceManager.getDefault(SignalHeadManager.class),
2602            null, DisplayOptions.DISPLAYNAME);
2603
2604    private final JCheckBox setA1Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2605    private final JCheckBox setA2Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2606    private final JCheckBox setB1Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2607    private final JCheckBox setB2Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2608    private final JCheckBox setC1Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2609    private final JCheckBox setC2Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2610    private final JCheckBox setD1Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2611    private final JCheckBox setD2Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2612
2613    private final JCheckBox setupA1Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2614    private final JCheckBox setupA2Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2615    private final JCheckBox setupB1Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2616    private final JCheckBox setupB2Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2617    private final JCheckBox setupC1Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2618    private final JCheckBox setupC2Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2619    private final JCheckBox setupD1Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2620    private final JCheckBox setupD2Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2621
2622    private JButton getSavedXoverSignalHeads = null;
2623    private JButton changeXoverSignalIcon = null;
2624    private JButton setXoverSignalsDone = null;
2625    private JButton setXoverSignalsCancel = null;
2626
2627    private SignalHead a1Head = null;
2628    private SignalHead a2Head = null;
2629    private SignalHead b1Head = null;
2630    private SignalHead b2Head = null;
2631    private SignalHead c1Head = null;
2632    private SignalHead c2Head = null;
2633    private SignalHead d1Head = null;
2634    private SignalHead d2Head = null;
2635
2636    private LayoutTurnout.TurnoutType xoverType = LayoutTurnout.TurnoutType.DOUBLE_XOVER; // changes to RH_XOVER or LH_XOVER as required
2637    private LayoutTurnout.TurnoutType xoverCurr = LayoutTurnout.TurnoutType.NONE;         // Controls creating the frame
2638    private String xoverTurnoutName = "";
2639    private final JLabel xoverTurnoutNameLabel = new JLabel("");
2640
2641    //display dialog for Set Signals at Crossover Turnout tool
2642    public void setSignalsAtXoverTurnoutFromMenu(@Nonnull LayoutTurnout to,
2643            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
2644        layoutTurnout = to;
2645        turnout = to.getTurnout();
2646        xoverType = layoutTurnout.getTurnoutType();
2647        if ((xoverType != LayoutTurnout.TurnoutType.DOUBLE_XOVER) && (xoverType != LayoutTurnout.TurnoutType.RH_XOVER)
2648                && (xoverType != LayoutTurnout.TurnoutType.LH_XOVER)) {
2649            log.error("entered Set Signals at XOver, with a non-crossover turnout");
2650            return;
2651        }
2652        xoverTurnoutName = layoutTurnout.getTurnoutName();
2653        setSignalsAtXoverTurnoutFromMenuFlag = true;
2654        setSignalsAtXoverTurnout(theEditor, theFrame);
2655        setSignalsAtXoverTurnoutFromMenuFlag = false;
2656    }
2657
2658    public void setSignalsAtXoverTurnout(@Nonnull MultiIconEditor theEditor,
2659            @Nonnull JFrame theFrame) {
2660        signalIconEditor = theEditor;
2661        signalFrame = theFrame;
2662
2663        if (!setSignalsAtXoverTurnoutFromMenuFlag) {
2664
2665            List<LayoutTurnout> xovers = new ArrayList<>();
2666            for (LayoutTurnout layoutTurnout : layoutEditor.getLayoutTurnouts()) {
2667                if (layoutTurnout.isTurnoutTypeXover()) {
2668                    xovers.add(layoutTurnout);
2669                }
2670            }
2671            JComboBox<LayoutTurnout> jcb = new JComboBox<>(
2672                    xovers.toArray(new LayoutTurnout[xovers.size()]));
2673            jcb.setEditable(true);
2674            JOptionPane.showMessageDialog(layoutEditor, jcb,
2675                    Bundle.getMessage("MakeLabel",
2676                            Bundle.getMessage("EnterXOverTurnout")),
2677                    JOptionPane.QUESTION_MESSAGE);
2678            LayoutTurnout layoutTurnout = (LayoutTurnout) jcb.getSelectedItem();
2679            xoverTurnoutName = layoutTurnout.getTurnoutName();
2680
2681            if (xoverTurnoutName.length() < 3) {
2682                return;
2683            }
2684        }
2685
2686        if (!getTurnoutInformation(true)) {
2687            return;
2688        }
2689
2690        //Initialize if needed which can be the first time or the crossover type has changed.
2691        if (setSignalsAtXoverTurnoutFrame == null || xoverCurr != xoverType) {
2692            xoverCurr = xoverType;
2693            setSignalsAtXoverTurnoutOpenFlag = false;
2694            setSignalsAtXoverTurnoutFrame = new JmriJFrame(Bundle.getMessage("SignalsAtXoverTurnout"), false, true);
2695            oneFrameToRuleThemAll(setSignalsAtXoverTurnoutFrame);
2696            setSignalsAtXoverTurnoutFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
2697            setSignalsAtXoverTurnoutFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtXoverTurnout", true);
2698            setSignalsAtXoverTurnoutFrame.setLocation(70, 30);
2699            Container theContentPane = setSignalsAtXoverTurnoutFrame.getContentPane();
2700            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
2701
2702            JPanel panel1 = new JPanel(new FlowLayout());
2703            panel1.add(xoverTurnoutNameLabel);
2704            theContentPane.add(panel1);
2705            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
2706
2707            JPanel panel2 = new JPanel(new FlowLayout());
2708            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
2709            panel2.add(shTitle);
2710            panel2.add(new JLabel("   "));
2711            panel2.add(getSavedXoverSignalHeads = new JButton(Bundle.getMessage("GetSaved")));
2712            getSavedXoverSignalHeads.addActionListener(this::xoverTurnoutSignalsGetSaved);
2713            getSavedXoverSignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
2714            theContentPane.add(panel2);
2715
2716            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
2717            JPanel panel2a = new JPanel(new FlowLayout());
2718            panel2a.add(new JLabel("   "));
2719            panel2a.add(setPlaceAllHeads);
2720            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
2721            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
2722                boolean isSelected = setPlaceAllHeads.isSelected();
2723                //(de)select all checkboxes
2724                setA1Head.setSelected(isSelected);
2725                setA2Head.setSelected(isSelected);
2726                setB1Head.setSelected(isSelected);
2727                setB2Head.setSelected(isSelected);
2728                setC1Head.setSelected(isSelected);
2729                setC2Head.setSelected(isSelected);
2730                setD1Head.setSelected(isSelected);
2731                setD2Head.setSelected(isSelected);
2732            });
2733            panel2a.add(new JLabel("  "));
2734            panel2a.add(setupAllLogic);
2735            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
2736            setupAllLogic.addActionListener((ActionEvent e) -> {
2737                boolean isSelected = setupAllLogic.isSelected();
2738                //(de)select all checkboxes
2739                setupA1Logic.setSelected(isSelected);
2740                setupA2Logic.setSelected(isSelected);
2741                setupB1Logic.setSelected(isSelected);
2742                setupB2Logic.setSelected(isSelected);
2743                setupC1Logic.setSelected(isSelected);
2744                setupC2Logic.setSelected(isSelected);
2745                setupD1Logic.setSelected(isSelected);
2746                setupD2Logic.setSelected(isSelected);
2747            });
2748            theContentPane.add(panel2a);
2749
2750            JPanel panel21 = new JPanel(new FlowLayout());
2751            JLabel a1Label = new JLabel(
2752                    Bundle.getMessage("MakeLabel",
2753                            Bundle.getMessage("XContinuing", "A")));
2754            panel21.add(a1Label);
2755            panel21.add(a1SignalHeadComboBox);
2756            theContentPane.add(panel21);
2757            a1SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2758
2759            JPanel panel22 = new JPanel(new FlowLayout());
2760            panel22.add(new JLabel("   "));
2761            panel22.add(setA1Head);
2762            setA1Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2763            panel22.add(new JLabel("  "));
2764            panel22.add(setupA1Logic);
2765            setupA1Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2766            theContentPane.add(panel22);
2767            if (!(xoverType == LayoutTurnout.TurnoutType.LH_XOVER)) {
2768                JPanel panel23 = new JPanel(new FlowLayout());
2769                JLabel a2Label = new JLabel(Bundle.getMessage("MakeLabel",
2770                        Bundle.getMessage("XDiverging", "A")));
2771                panel23.add(a2Label);
2772                panel23.add(a2SignalHeadComboBox);
2773                theContentPane.add(panel23);
2774                a2SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2775                JPanel panel24 = new JPanel(new FlowLayout());
2776                panel24.add(new JLabel("   "));
2777                panel24.add(setA2Head);
2778                setA2Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2779                panel24.add(new JLabel("  "));
2780                panel24.add(setupA2Logic);
2781                setupA2Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2782                theContentPane.add(panel24);
2783            }
2784
2785            JPanel panel31 = new JPanel(new FlowLayout());
2786            JLabel b1Label = new JLabel(Bundle.getMessage("MakeLabel",
2787                    Bundle.getMessage("XContinuing", "B")));
2788            panel31.add(b1Label);
2789            panel31.add(b1SignalHeadComboBox);
2790            theContentPane.add(panel31);
2791            b1SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2792
2793            JPanel panel32 = new JPanel(new FlowLayout());
2794            panel32.add(new JLabel("   "));
2795            panel32.add(setB1Head);
2796            setB1Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2797            panel32.add(new JLabel("  "));
2798            panel32.add(setupB1Logic);
2799            setupB1Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2800            theContentPane.add(panel32);
2801            if (!(xoverType == LayoutTurnout.TurnoutType.RH_XOVER)) {
2802                JPanel panel33 = new JPanel(new FlowLayout());
2803                JLabel b2Label = new JLabel(Bundle.getMessage("MakeLabel",
2804                        Bundle.getMessage("XDiverging", "B")));
2805                panel33.add(b2Label);
2806                panel33.add(b2SignalHeadComboBox);
2807                theContentPane.add(panel33);
2808                b2SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2809                JPanel panel34 = new JPanel(new FlowLayout());
2810                panel34.add(new JLabel("   "));
2811                panel34.add(setB2Head);
2812                setB2Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2813                panel34.add(new JLabel("  "));
2814                panel34.add(setupB2Logic);
2815                setupB2Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2816                theContentPane.add(panel34);
2817            }
2818
2819            JPanel panel41 = new JPanel(new FlowLayout());
2820            JLabel c1Label = new JLabel(Bundle.getMessage("MakeLabel",
2821                    Bundle.getMessage("XContinuing", "C")));
2822            panel41.add(c1Label);
2823            panel41.add(c1SignalHeadComboBox);
2824            theContentPane.add(panel41);
2825            c1SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2826
2827            JPanel panel42 = new JPanel(new FlowLayout());
2828            panel42.add(new JLabel("   "));
2829            panel42.add(setC1Head);
2830            setC1Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2831            panel42.add(new JLabel("  "));
2832            panel42.add(setupC1Logic);
2833            setupC1Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2834            theContentPane.add(panel42);
2835            if (!(xoverType == LayoutTurnout.TurnoutType.LH_XOVER)) {
2836                JPanel panel43 = new JPanel(new FlowLayout());
2837                JLabel c2Label = new JLabel(Bundle.getMessage("MakeLabel",
2838                        Bundle.getMessage("XDiverging", "C")));
2839                panel43.add(c2Label);
2840                panel43.add(c2SignalHeadComboBox);
2841                theContentPane.add(panel43);
2842                c2SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2843                JPanel panel44 = new JPanel(new FlowLayout());
2844                panel44.add(new JLabel("   "));
2845                panel44.add(setC2Head);
2846                setC2Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2847                panel44.add(new JLabel("  "));
2848                panel44.add(setupC2Logic);
2849                setupC2Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2850                theContentPane.add(panel44);
2851            }
2852
2853            JPanel panel51 = new JPanel(new FlowLayout());
2854            JLabel d1Label = new JLabel(Bundle.getMessage("MakeLabel",
2855                    Bundle.getMessage("XContinuing", "D")));
2856            panel51.add(d1Label);
2857            panel51.add(d1SignalHeadComboBox);
2858            theContentPane.add(panel51);
2859            d1SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2860
2861            JPanel panel52 = new JPanel(new FlowLayout());
2862            panel52.add(new JLabel("   "));
2863            panel52.add(setD1Head);
2864            setD1Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2865            panel52.add(new JLabel("  "));
2866            panel52.add(setupD1Logic);
2867            setupD1Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2868            theContentPane.add(panel52);
2869            if (xoverType != LayoutTurnout.TurnoutType.RH_XOVER) {
2870                JPanel panel53 = new JPanel(new FlowLayout());
2871                JLabel d2Label = new JLabel(Bundle.getMessage("MakeLabel",
2872                        Bundle.getMessage("XDiverging", "D")));
2873                panel53.add(d2Label);
2874                panel53.add(d2SignalHeadComboBox);
2875                theContentPane.add(panel53);
2876                d2SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2877                JPanel panel54 = new JPanel(new FlowLayout());
2878                panel54.add(new JLabel("   "));
2879                panel54.add(setD2Head);
2880                setD2Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2881                panel54.add(new JLabel("  "));
2882                panel54.add(setupD2Logic);
2883                setupD2Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2884                theContentPane.add(panel54);
2885            }
2886            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
2887
2888            JPanel panel6 = new JPanel(new FlowLayout());
2889            panel6.add(changeXoverSignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
2890            changeXoverSignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
2891            changeXoverSignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
2892            panel6.add(new JLabel("   "));
2893            panel6.add(setXoverSignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
2894            setXoverSignalsDone.addActionListener(this::setXoverSignalsDonePressed);
2895            setXoverSignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
2896
2897            panel6.add(setXoverSignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
2898            setXoverSignalsCancel.addActionListener(this::setXoverSignalsCancelPressed);
2899            setXoverSignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
2900            theContentPane.add(panel6);
2901
2902            //make this button the default button (return or enter activates)
2903            JRootPane rootPane = SwingUtilities.getRootPane(setXoverSignalsDone);
2904            if (rootPane != null) {
2905                rootPane.setDefaultButton(setXoverSignalsDone);
2906            }
2907
2908            setSignalsAtXoverTurnoutFrame.addWindowListener(new WindowAdapter() {
2909                @Override
2910                public void windowClosing(WindowEvent e) {
2911                    setXoverSignalsCancelPressed(null);
2912                }
2913            });
2914        }
2915        setPlaceAllHeads.setSelected(false);
2916        setupAllLogic.setSelected(false);
2917
2918        xoverTurnoutNameLabel.setText(Bundle.getMessage("MakeLabel",
2919                Bundle.getMessage("BeanNameTurnout")
2920                + " " + Bundle.getMessage("Name")) + xoverTurnoutName);
2921        xoverType = layoutTurnout.getTurnoutType();
2922
2923        xoverTurnoutSignalsGetSaved(null);
2924
2925        if (!setSignalsAtXoverTurnoutOpenFlag) {
2926            setSignalsAtXoverTurnoutFrame.setPreferredSize(null);
2927            setSignalsAtXoverTurnoutFrame.pack();
2928            setSignalsAtXoverTurnoutOpenFlag = true;
2929        }
2930        setSignalsAtXoverTurnoutFrame.setVisible(true);
2931    }   //setSignalsAtXoverTurnout
2932
2933    private void xoverTurnoutSignalsGetSaved(ActionEvent a) {
2934        a1SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalA1());
2935        a2SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalA2());
2936        b1SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalB1());
2937        b2SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalB2());
2938        c1SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalC1());
2939        c2SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalC2());
2940        d1SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalD1());
2941        d2SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalD2());
2942    }
2943
2944    private void setXoverSignalsCancelPressed(ActionEvent a) {
2945        setSignalsAtXoverTurnoutOpenFlag = false;
2946        setSignalsAtXoverTurnoutFrame.setVisible(false);
2947    }
2948
2949    private void setXoverSignalsDonePressed(ActionEvent a) {
2950        if (!getXoverSignalHeadInformation()) {
2951            return;
2952        }
2953        //place signal icons if requested, and assign signal heads to this turnout
2954        String signalHeadName = a1SignalHeadComboBox.getSelectedItemDisplayName();
2955        if (signalHeadName == null) {
2956            signalHeadName = "";
2957        }
2958        if (setA1Head.isSelected()) {
2959            if (isHeadOnPanel(a1Head)
2960                    && (a1Head != getHeadFromName(layoutTurnout.getSignalA1Name()))) {
2961                JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
2962                        Bundle.getMessage("SignalsError6",
2963                                new Object[]{signalHeadName}),
2964                        Bundle.getMessage("ErrorTitle"),
2965                        JOptionPane.ERROR_MESSAGE);
2966                return;
2967            } else {
2968                removeSignalHeadFromPanel(layoutTurnout.getSignalA1Name());
2969                placeA1();
2970                removeAssignment(a1Head);
2971                layoutTurnout.setSignalA1Name(signalHeadName);
2972                needRedraw = true;
2973            }
2974        } else {
2975            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a1Head, layoutTurnout);
2976            if (assigned == LayoutTurnout.Geometry.NONE) {
2977                if (isHeadOnPanel(a1Head)
2978                        && isHeadAssignedAnywhere(a1Head)) {
2979                    JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
2980                            Bundle.getMessage("SignalsError8",
2981                                    new Object[]{signalHeadName}),
2982                            Bundle.getMessage("ErrorTitle"),
2983                            JOptionPane.ERROR_MESSAGE);
2984                    return;
2985                } else {
2986                    removeSignalHeadFromPanel(layoutTurnout.getSignalA1Name());
2987                    removeAssignment(a1Head);
2988                    layoutTurnout.setSignalA1Name(signalHeadName);
2989                }
2990                //} else if (assigned != A1) {
2991                //need to figure out what to do in this case.
2992            }
2993        }
2994        signalHeadName = a2SignalHeadComboBox.getSelectedItemDisplayName();
2995        if (signalHeadName == null) {
2996            signalHeadName = "";
2997        }
2998        if ((a2Head != null) && setA2Head.isSelected()) {
2999            if (isHeadOnPanel(a2Head)
3000                    && (a2Head != getHeadFromName(layoutTurnout.getSignalA2Name()))) {
3001                JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3002                        Bundle.getMessage("SignalsError6",
3003                                new Object[]{signalHeadName}),
3004                        Bundle.getMessage("ErrorTitle"),
3005                        JOptionPane.ERROR_MESSAGE);
3006                return;
3007            } else {
3008                removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
3009                placeA2();
3010                removeAssignment(a2Head);
3011                layoutTurnout.setSignalA2Name(signalHeadName);
3012                needRedraw = true;
3013            }
3014        } else if (a2Head != null) {
3015            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a2Head, layoutTurnout);
3016            if (assigned == LayoutTurnout.Geometry.NONE) {
3017                if (isHeadOnPanel(a2Head)
3018                        && isHeadAssignedAnywhere(a2Head)) {
3019                    JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3020                            Bundle.getMessage("SignalsError8",
3021                                    new Object[]{signalHeadName}),
3022                            Bundle.getMessage("ErrorTitle"),
3023                            JOptionPane.ERROR_MESSAGE);
3024                    return;
3025                } else {
3026                    removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
3027                    removeAssignment(a2Head);
3028                    layoutTurnout.setSignalA2Name(signalHeadName);
3029                }
3030                //} else if (assigned != A2) {
3031                //need to figure out what to do in this case.
3032            }
3033        } else { //a2Head known to be null here
3034            removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
3035            layoutTurnout.setSignalA2Name("");
3036        }
3037        signalHeadName = b1SignalHeadComboBox.getSelectedItemDisplayName();
3038        if (signalHeadName == null) {
3039            signalHeadName = "";
3040        }
3041        if (setB1Head.isSelected()) {
3042            if (isHeadOnPanel(b1Head)
3043                    && (b1Head != getHeadFromName(layoutTurnout.getSignalB1Name()))) {
3044                JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3045                        Bundle.getMessage("SignalsError6",
3046                                new Object[]{signalHeadName}),
3047                        Bundle.getMessage("ErrorTitle"),
3048                        JOptionPane.ERROR_MESSAGE);
3049                return;
3050            } else {
3051                removeSignalHeadFromPanel(layoutTurnout.getSignalB1Name());
3052                placeB1();
3053                removeAssignment(b1Head);
3054                layoutTurnout.setSignalB1Name(signalHeadName);
3055                needRedraw = true;
3056            }
3057        } else {
3058            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b1Head, layoutTurnout);
3059            if (assigned == LayoutTurnout.Geometry.NONE) {
3060                if (isHeadOnPanel(b1Head)
3061                        && isHeadAssignedAnywhere(b1Head)) {
3062                    JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3063                            Bundle.getMessage("SignalsError8",
3064                                    new Object[]{signalHeadName}),
3065                            Bundle.getMessage("ErrorTitle"),
3066                            JOptionPane.ERROR_MESSAGE);
3067                    return;
3068                } else {
3069                    removeSignalHeadFromPanel(layoutTurnout.getSignalB1Name());
3070                    removeAssignment(b1Head);
3071                    layoutTurnout.setSignalB1Name(signalHeadName);
3072                }
3073                //} else if (assigned != B1) {
3074                //need to figure out what to do in this case.
3075            }
3076        }
3077        signalHeadName = b2SignalHeadComboBox.getSelectedItemDisplayName();
3078        if (signalHeadName == null) {
3079            signalHeadName = "";
3080        }
3081        if ((b2Head != null) && setB2Head.isSelected()) {
3082            if (isHeadOnPanel(b2Head)
3083                    && (b2Head != getHeadFromName(layoutTurnout.getSignalB2Name()))) {
3084                JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3085                        Bundle.getMessage("SignalsError6",
3086                                new Object[]{signalHeadName}),
3087                        Bundle.getMessage("ErrorTitle"),
3088                        JOptionPane.ERROR_MESSAGE);
3089                return;
3090            } else {
3091                removeSignalHeadFromPanel(layoutTurnout.getSignalB2Name());
3092                placeB2();
3093                removeAssignment(b2Head);
3094                layoutTurnout.setSignalB2Name(signalHeadName);
3095                needRedraw = true;
3096            }
3097        } else if (b2Head != null) {
3098            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b2Head, layoutTurnout);
3099            if (assigned == LayoutTurnout.Geometry.NONE) {
3100                if (isHeadOnPanel(b2Head)
3101                        && isHeadAssignedAnywhere(b2Head)) {
3102                    JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3103                            Bundle.getMessage("SignalsError8",
3104                                    new Object[]{signalHeadName}),
3105                            Bundle.getMessage("ErrorTitle"),
3106                            JOptionPane.ERROR_MESSAGE);
3107                    return;
3108                } else {
3109                    removeSignalHeadFromPanel(layoutTurnout.getSignalB2Name());
3110                    removeAssignment(b2Head);
3111                    layoutTurnout.setSignalB2Name(signalHeadName);
3112                }
3113                //} else if (assigned != B2) {
3114                //need to figure out what to do in this case.
3115            }
3116        } else { //b2Head known to be null here
3117            removeSignalHeadFromPanel(layoutTurnout.getSignalB2Name());
3118            layoutTurnout.setSignalB2Name("");
3119        }
3120        signalHeadName = c1SignalHeadComboBox.getSelectedItemDisplayName();
3121        if (signalHeadName == null) {
3122            signalHeadName = "";
3123        }
3124        if (setC1Head.isSelected()) {
3125            if (isHeadOnPanel(c1Head)
3126                    && (c1Head != getHeadFromName(layoutTurnout.getSignalC1Name()))) {
3127                JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3128                        Bundle.getMessage("SignalsError6",
3129                                new Object[]{signalHeadName}),
3130                        Bundle.getMessage("ErrorTitle"),
3131                        JOptionPane.ERROR_MESSAGE);
3132                return;
3133            } else {
3134                removeSignalHeadFromPanel(layoutTurnout.getSignalC1Name());
3135                placeC1();
3136                removeAssignment(c1Head);
3137                layoutTurnout.setSignalC1Name(signalHeadName);
3138                needRedraw = true;
3139            }
3140        } else {
3141            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c1Head, layoutTurnout);
3142            if (assigned == LayoutTurnout.Geometry.NONE) {
3143                if (isHeadOnPanel(c1Head)
3144                        && isHeadAssignedAnywhere(c1Head)) {
3145                    JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3146                            Bundle.getMessage("SignalsError8",
3147                                    new Object[]{signalHeadName}),
3148                            Bundle.getMessage("ErrorTitle"),
3149                            JOptionPane.ERROR_MESSAGE);
3150                    return;
3151                } else {
3152                    removeSignalHeadFromPanel(layoutTurnout.getSignalC1Name());
3153                    removeAssignment(c1Head);
3154                    layoutTurnout.setSignalC1Name(signalHeadName);
3155                }
3156                //} else if (assigned != C1) {
3157                //need to figure out what to do in this case.
3158            }
3159        }
3160        signalHeadName = c2SignalHeadComboBox.getSelectedItemDisplayName();
3161        if (signalHeadName == null) {
3162            signalHeadName = "";
3163        }
3164        if ((c2Head != null) && setC2Head.isSelected()) {
3165            if (isHeadOnPanel(c2Head)
3166                    && (c2Head != getHeadFromName(layoutTurnout.getSignalC2Name()))) {
3167                JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3168                        Bundle.getMessage("SignalsError6",
3169                                new Object[]{signalHeadName}),
3170                        Bundle.getMessage("ErrorTitle"),
3171                        JOptionPane.ERROR_MESSAGE);
3172                return;
3173            } else {
3174                removeSignalHeadFromPanel(layoutTurnout.getSignalC2Name());
3175                placeC2();
3176                removeAssignment(c2Head);
3177                layoutTurnout.setSignalC2Name(signalHeadName);
3178                needRedraw = true;
3179            }
3180        } else if (c2Head != null) {
3181            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c2Head, layoutTurnout);
3182            if (assigned == LayoutTurnout.Geometry.NONE) {
3183                if (isHeadOnPanel(c2Head)
3184                        && isHeadAssignedAnywhere(c2Head)) {
3185                    JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3186                            Bundle.getMessage("SignalsError8",
3187                                    new Object[]{signalHeadName}),
3188                            Bundle.getMessage("ErrorTitle"),
3189                            JOptionPane.ERROR_MESSAGE);
3190                    return;
3191                } else {
3192                    removeSignalHeadFromPanel(layoutTurnout.getSignalC2Name());
3193                    removeAssignment(c2Head);
3194                    layoutTurnout.setSignalC2Name(signalHeadName);
3195                }
3196                //} else if (assigned != C2) {
3197                //need to figure out what to do in this case.
3198            }
3199        } else { //c2Head known to be null here
3200            removeSignalHeadFromPanel(layoutTurnout.getSignalC2Name());
3201            layoutTurnout.setSignalC2Name("");
3202        }
3203        signalHeadName = d1SignalHeadComboBox.getSelectedItemDisplayName();
3204        if (signalHeadName == null) {
3205            signalHeadName = "";
3206        }
3207        if (setD1Head.isSelected()) {
3208            if (isHeadOnPanel(d1Head)
3209                    && (d1Head != getHeadFromName(layoutTurnout.getSignalD1Name()))) {
3210                JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3211                        Bundle.getMessage("SignalsError6",
3212                                new Object[]{signalHeadName}),
3213                        Bundle.getMessage("ErrorTitle"),
3214                        JOptionPane.ERROR_MESSAGE);
3215                return;
3216            } else {
3217                removeSignalHeadFromPanel(layoutTurnout.getSignalD1Name());
3218                placeD1();
3219                removeAssignment(d1Head);
3220                layoutTurnout.setSignalD1Name(signalHeadName);
3221                needRedraw = true;
3222            }
3223        } else {
3224            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d1Head, layoutTurnout);
3225            if (assigned == LayoutTurnout.Geometry.NONE) {
3226                if (isHeadOnPanel(d1Head)
3227                        && isHeadAssignedAnywhere(d1Head)) {
3228                    JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3229                            Bundle.getMessage("SignalsError8",
3230                                    new Object[]{signalHeadName}),
3231                            Bundle.getMessage("ErrorTitle"),
3232                            JOptionPane.ERROR_MESSAGE);
3233                    return;
3234                } else {
3235                    removeSignalHeadFromPanel(layoutTurnout.getSignalD1Name());
3236                    removeAssignment(d1Head);
3237                    layoutTurnout.setSignalD1Name(signalHeadName);
3238                }
3239                //} else if (assigned != D1) {
3240                //need to figure out what to do in this case.
3241            }
3242        }
3243        signalHeadName = d2SignalHeadComboBox.getSelectedItemDisplayName();
3244        if (signalHeadName == null) {
3245            signalHeadName = "";
3246        }
3247        if ((d2Head != null) && setD2Head.isSelected()) {
3248            if (isHeadOnPanel(d2Head)
3249                    && (d2Head != getHeadFromName(layoutTurnout.getSignalD2Name()))) {
3250                JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3251                        Bundle.getMessage("SignalsError6",
3252                                new Object[]{signalHeadName}),
3253                        Bundle.getMessage("ErrorTitle"),
3254                        JOptionPane.ERROR_MESSAGE);
3255                return;
3256            } else {
3257                removeSignalHeadFromPanel(layoutTurnout.getSignalD2Name());
3258                placeD2();
3259                removeAssignment(d2Head);
3260                layoutTurnout.setSignalD2Name(signalHeadName);
3261                needRedraw = true;
3262            }
3263        } else if (d2Head != null) {
3264            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d2Head, layoutTurnout);
3265            if (assigned == LayoutTurnout.Geometry.NONE) {
3266                if (isHeadOnPanel(d2Head)
3267                        && isHeadAssignedAnywhere(d2Head)) {
3268                    JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3269                            Bundle.getMessage("SignalsError8",
3270                                    new Object[]{signalHeadName}),
3271                            Bundle.getMessage("ErrorTitle"),
3272                            JOptionPane.ERROR_MESSAGE);
3273                    return;
3274                } else {
3275                    removeSignalHeadFromPanel(layoutTurnout.getSignalD2Name());
3276                    removeAssignment(d2Head);
3277                    layoutTurnout.setSignalD2Name(signalHeadName);
3278                }
3279                //} else if (assigned != D2) {
3280                //need to figure out what to do in this case.
3281            }
3282        } else { //d2Head known to be null here
3283            removeSignalHeadFromPanel(layoutTurnout.getSignalD2Name());
3284            layoutTurnout.setSignalD2Name("");
3285        }
3286        //setup logic if requested
3287        if (setupA1Logic.isSelected() || setupA2Logic.isSelected()) {
3288            if (xoverType == LayoutTurnout.TurnoutType.LH_XOVER) {
3289                setLogicXoverContinuing(a1Head, (TrackSegment) layoutTurnout.getConnectB());
3290            } else {
3291                setLogicXover(a1Head, (TrackSegment) layoutTurnout.getConnectB(), a2Head,
3292                        (TrackSegment) layoutTurnout.getConnectC(), setupA1Logic.isSelected(),
3293                        setupA2Logic.isSelected());
3294            }
3295        }
3296        if (setupB1Logic.isSelected() || setupB2Logic.isSelected()) {
3297            if (xoverType == LayoutTurnout.TurnoutType.RH_XOVER) {
3298                setLogicXoverContinuing(b1Head, (TrackSegment) layoutTurnout.getConnectA());
3299            } else {
3300                setLogicXover(b1Head, (TrackSegment) layoutTurnout.getConnectA(), b2Head,
3301                        (TrackSegment) layoutTurnout.getConnectD(), setupB1Logic.isSelected(),
3302                        setupB2Logic.isSelected());
3303            }
3304        }
3305        if (setupC1Logic.isSelected() || setupC2Logic.isSelected()) {
3306            if (xoverType == LayoutTurnout.TurnoutType.LH_XOVER) {
3307                setLogicXoverContinuing(c1Head, (TrackSegment) layoutTurnout.getConnectD());
3308            } else {
3309                setLogicXover(c1Head, (TrackSegment) layoutTurnout.getConnectD(), c2Head,
3310                        (TrackSegment) layoutTurnout.getConnectA(), setupC1Logic.isSelected(),
3311                        setupC2Logic.isSelected());
3312            }
3313        }
3314        if (setupD1Logic.isSelected() || setupD2Logic.isSelected()) {
3315            if (xoverType == LayoutTurnout.TurnoutType.RH_XOVER) {
3316                setLogicXoverContinuing(d1Head, (TrackSegment) layoutTurnout.getConnectC());
3317            } else {
3318                setLogicXover(d1Head, (TrackSegment) layoutTurnout.getConnectC(), d2Head,
3319                        (TrackSegment) layoutTurnout.getConnectB(), setupD1Logic.isSelected(),
3320                        setupD2Logic.isSelected());
3321            }
3322        }
3323        //make sure this layout turnout is not linked to another
3324        layoutTurnout.setLinkType(LayoutTurnout.LinkType.NO_LINK);
3325        layoutTurnout.setLinkedTurnoutName("");
3326        //finish up
3327        setSignalsAtXoverTurnoutOpenFlag = false;
3328        setSignalsAtXoverTurnoutFrame.setVisible(false);
3329        if (needRedraw) {
3330            layoutEditor.redrawPanel();
3331            needRedraw = false;
3332            layoutEditor.setDirty();
3333        }
3334    }   //setXoverSignalsDonePressed
3335
3336    private boolean getXoverSignalHeadInformation() {
3337        a1Head = getSignalHeadFromEntry(a1SignalHeadComboBox, true, setSignalsAtXoverTurnoutFrame);
3338        if (a1Head == null) {
3339            return false;
3340        }
3341        if (!(xoverType == LayoutTurnout.TurnoutType.LH_XOVER)) {
3342            a2Head = getSignalHeadFromEntry(a2SignalHeadComboBox, false, setSignalsAtXoverTurnoutFrame);
3343        } else {
3344            a2Head = null;
3345        }
3346        b1Head = getSignalHeadFromEntry(b1SignalHeadComboBox, true, setSignalsAtXoverTurnoutFrame);
3347        if (b1Head == null) {
3348            return false;
3349        }
3350        if (!(xoverType == LayoutTurnout.TurnoutType.RH_XOVER)) {
3351            b2Head = getSignalHeadFromEntry(b2SignalHeadComboBox, false, setSignalsAtXoverTurnoutFrame);
3352        } else {
3353            b2Head = null;
3354        }
3355        c1Head = getSignalHeadFromEntry(c1SignalHeadComboBox, true, setSignalsAtXoverTurnoutFrame);
3356        if (c1Head == null) {
3357            return false;
3358        }
3359        if (!(xoverType == LayoutTurnout.TurnoutType.LH_XOVER)) {
3360            c2Head = getSignalHeadFromEntry(c2SignalHeadComboBox, false, setSignalsAtXoverTurnoutFrame);
3361        } else {
3362            c2Head = null;
3363        }
3364        d1Head = getSignalHeadFromEntry(d1SignalHeadComboBox, true, setSignalsAtXoverTurnoutFrame);
3365        if (d1Head == null) {
3366            return false;
3367        }
3368        if (!(xoverType == LayoutTurnout.TurnoutType.RH_XOVER)) {
3369            d2Head = getSignalHeadFromEntry(d2SignalHeadComboBox, false, setSignalsAtXoverTurnoutFrame);
3370        } else {
3371            d2Head = null;
3372        }
3373        return true;
3374    }
3375
3376    private void placeA1() {
3377        if (testIcon == null) {
3378            testIcon = signalIconEditor.getIcon(0);
3379        }
3380        String signalHeadName = a1SignalHeadComboBox.getSelectedItemDisplayName();
3381        if (signalHeadName == null) {
3382            signalHeadName = "";
3383        }
3384        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3385
3386        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3387
3388        Point2D coordsA = layoutTurnoutView.getCoordsA();
3389        Point2D delta = new Point2D.Double(0.0, +shift);
3390
3391        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3392        Point2D where = MathUtil.add(coordsA, delta);
3393        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
3394    }
3395
3396    private void placeA2() {
3397        if (testIcon == null) {
3398            testIcon = signalIconEditor.getIcon(0);
3399        }
3400        String signalHeadName = a2SignalHeadComboBox.getSelectedItemDisplayName();
3401        if (signalHeadName == null) {
3402            signalHeadName = "";
3403        }
3404        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3405
3406        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3407
3408        Point2D coordsA = layoutTurnoutView.getCoordsA();
3409        Point2D delta = new Point2D.Double(-2.0 * shift, +shift);
3410
3411        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3412        Point2D where = MathUtil.add(coordsA, delta);
3413        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
3414    }
3415
3416    private void placeB1() {
3417        if (testIcon == null) {
3418            testIcon = signalIconEditor.getIcon(0);
3419        }
3420        String signalHeadName = b1SignalHeadComboBox.getSelectedItemDisplayName();
3421        if (signalHeadName == null) {
3422            signalHeadName = "";
3423        }
3424        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3425
3426        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3427
3428        Point2D coordsB = layoutTurnoutView.getCoordsB();
3429        Point2D delta = new Point2D.Double(-shift, -shift);
3430
3431        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3432        Point2D where = MathUtil.add(coordsB, delta);
3433        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
3434    }
3435
3436    private void placeB2() {
3437        if (testIcon == null) {
3438            testIcon = signalIconEditor.getIcon(0);
3439        }
3440        String signalHeadName = b2SignalHeadComboBox.getSelectedItemDisplayName();
3441        if (signalHeadName == null) {
3442            signalHeadName = "";
3443        }
3444        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3445
3446        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3447
3448        Point2D coordsB = layoutTurnoutView.getCoordsB();
3449        Point2D delta = new Point2D.Double(+shift, -shift);
3450
3451        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3452        Point2D where = MathUtil.add(coordsB, delta);
3453        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
3454    }
3455
3456    private void placeC1() {
3457        if (testIcon == null) {
3458            testIcon = signalIconEditor.getIcon(0);
3459        }
3460        String signalHeadName = c1SignalHeadComboBox.getSelectedItemDisplayName();
3461        if (signalHeadName == null) {
3462            signalHeadName = "";
3463        }
3464        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3465
3466        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3467
3468        Point2D coordsC = layoutTurnoutView.getCoordsC();
3469        Point2D delta = new Point2D.Double(0.0, -shift);
3470
3471        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3472        Point2D where = MathUtil.add(coordsC, delta);
3473        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
3474    }
3475
3476    private void placeC2() {
3477        if (testIcon == null) {
3478            testIcon = signalIconEditor.getIcon(0);
3479        }
3480        String signalHeadName = c2SignalHeadComboBox.getSelectedItemDisplayName();
3481        if (signalHeadName == null) {
3482            signalHeadName = "";
3483        }
3484        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3485
3486        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3487
3488        Point2D coordsC = layoutTurnoutView.getCoordsC();
3489        Point2D delta = new Point2D.Double(+2.0 * shift, -shift);
3490
3491        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3492        Point2D where = MathUtil.add(coordsC, delta);
3493        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
3494    }
3495
3496    private void placeD1() {
3497        if (testIcon == null) {
3498            testIcon = signalIconEditor.getIcon(0);
3499        }
3500        String signalHeadName = d1SignalHeadComboBox.getSelectedItemDisplayName();
3501        if (signalHeadName == null) {
3502            signalHeadName = "";
3503        }
3504        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3505
3506        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3507
3508        Point2D coordsD = layoutTurnoutView.getCoordsD();
3509        Point2D delta = new Point2D.Double(+shift, +shift);
3510
3511        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3512        Point2D where = MathUtil.add(coordsD, delta);
3513        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
3514    }
3515
3516    private void placeD2() {
3517        if (testIcon == null) {
3518            testIcon = signalIconEditor.getIcon(0);
3519        }
3520        String signalHeadName = d2SignalHeadComboBox.getSelectedItemDisplayName();
3521        if (signalHeadName == null) {
3522            signalHeadName = "";
3523        }
3524        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3525
3526        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3527
3528        Point2D coordsD = layoutTurnoutView.getCoordsD();
3529        Point2D delta = new Point2D.Double(-shift, +shift);
3530
3531        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3532        Point2D where = MathUtil.add(coordsD, delta);
3533        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
3534    }
3535
3536    @SuppressWarnings("null")
3537    private void setLogicXover(SignalHead head, TrackSegment track, SignalHead secondHead, TrackSegment track2,
3538            boolean setup1, boolean setup2) {
3539        if ((track == null) && setup1) {
3540            JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3541                    Bundle.getMessage("InfoMessage7"),
3542                    Bundle.getMessage("MessageTitle"),
3543                    JOptionPane.INFORMATION_MESSAGE);
3544            return;
3545        }
3546        Sensor occupancy = null;
3547        SignalHead nextHead = null;
3548        if ((track != null) && setup1) {
3549            LayoutBlock block = track.getLayoutBlock();
3550            if (block == null) {
3551                JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3552                        Bundle.getMessage("InfoMessage6"),
3553                        Bundle.getMessage("MessageTitle"),
3554                        JOptionPane.INFORMATION_MESSAGE);
3555                return;
3556            }
3557            occupancy = block.getOccupancySensor();
3558            if (occupancy == null) {
3559                JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3560                        Bundle.getMessage("InfoMessage4",
3561                                new Object[]{block.getUserName()}),
3562                        Bundle.getMessage("MessageTitle"),
3563                        JOptionPane.INFORMATION_MESSAGE);
3564                return;
3565            }
3566            nextHead = getNextSignalFromObject(track,
3567                    layoutTurnout, head.getSystemName(), setSignalsAtXoverTurnoutFrame);
3568            if ((nextHead == null) && (!reachedEndBumper())) {
3569                JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3570                        Bundle.getMessage("InfoMessage5",
3571                                new Object[]{block.getUserName()}),
3572                        Bundle.getMessage("MessageTitle"),
3573                        JOptionPane.INFORMATION_MESSAGE);
3574                return;
3575            }
3576            if (secondHead != null) {
3577                if (!initializeBlockBossLogic(head.getDisplayName())) {
3578                    return;
3579                }
3580                logic.setMode(BlockBossLogic.TRAILINGMAIN);
3581                logic.setTurnout(turnout.getDisplayName());
3582                logic.setSensor1(occupancy.getDisplayName());
3583                if (nextHead != null) {
3584                    logic.setWatchedSignal1(nextHead.getDisplayName(), false);
3585                }
3586                if (auxSignal != null) {
3587                    logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
3588                }
3589                finalizeBlockBossLogic();
3590            }
3591        }
3592        if ((secondHead != null) && !setup2) {
3593            return;
3594        }
3595        SignalHead savedAuxSignal = auxSignal;
3596        if (track2 == null) {
3597            JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3598                    Bundle.getMessage("InfoMessage7"),
3599                    Bundle.getMessage("MessageTitle"),
3600                    JOptionPane.INFORMATION_MESSAGE);
3601            return;
3602        }
3603        LayoutBlock block2 = track2.getLayoutBlock();
3604        if (block2 == null) {
3605            JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3606                    Bundle.getMessage("InfoMessage6"),
3607                    Bundle.getMessage("MessageTitle"),
3608                    JOptionPane.INFORMATION_MESSAGE);
3609            return;
3610        }
3611        Sensor occupancy2 = block2.getOccupancySensor();
3612        if (occupancy2 == null) {
3613            JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3614                    Bundle.getMessage("InfoMessage4",
3615                            new Object[]{block2.getUserName()}),
3616                    Bundle.getMessage("MessageTitle"),
3617                    JOptionPane.INFORMATION_MESSAGE);
3618            return;
3619        }
3620        String signalHeadName = head.getSystemName();
3621        if (secondHead != null) {
3622            signalHeadName = secondHead.getSystemName();
3623        }
3624        SignalHead nextHead2 = getNextSignalFromObject(track2,
3625                layoutTurnout, signalHeadName, setSignalsAtXoverTurnoutFrame);
3626        if ((nextHead2 == null) && (!reachedEndBumper())) {
3627            JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3628                    Bundle.getMessage("InfoMessage5",
3629                            new Object[]{block2.getUserName()}),
3630                    Bundle.getMessage("MessageTitle"),
3631                    JOptionPane.INFORMATION_MESSAGE);
3632            return;
3633        }
3634        if ((secondHead == null) && (track != null) && setup1) {
3635            if (!initializeBlockBossLogic(head.getDisplayName())) {
3636                return;
3637            }
3638            logic.setMode(BlockBossLogic.FACING);
3639            logic.setTurnout(turnout.getDisplayName());
3640            logic.setWatchedSensor1(occupancy.getDisplayName());
3641            logic.setWatchedSensor2(occupancy2.getDisplayName());
3642            if (nextHead != null) {
3643                logic.setWatchedSignal1(nextHead.getDisplayName(), false);
3644            }
3645            if (savedAuxSignal != null) {
3646                logic.setWatchedSignal1Alt(savedAuxSignal.getDisplayName());
3647            }
3648            if (nextHead2 != null) {
3649                logic.setWatchedSignal2(nextHead2.getDisplayName());
3650            }
3651            if (auxSignal != null) {
3652                logic.setWatchedSignal2Alt(auxSignal.getDisplayName());
3653            }
3654            logic.setLimitSpeed2(true);
3655            finalizeBlockBossLogic();
3656        } else if ((secondHead != null) && setup2) {
3657            if (!initializeBlockBossLogic(secondHead.getDisplayName())) {
3658                return;
3659            }
3660            logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
3661            logic.setTurnout(turnout.getDisplayName());
3662            logic.setSensor1(occupancy2.getDisplayName());
3663            if (nextHead2 != null) {
3664                logic.setWatchedSignal1(nextHead2.getDisplayName(), false);
3665            }
3666            if (auxSignal != null) {
3667                logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
3668            }
3669            logic.setLimitSpeed2(true);
3670            finalizeBlockBossLogic();
3671        }
3672    }   //setLogicXover
3673
3674    private void setLogicXoverContinuing(SignalHead head, TrackSegment track) {
3675        if (track == null) {
3676            JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3677                    Bundle.getMessage("InfoMessage7"),
3678                    Bundle.getMessage("MessageTitle"),
3679                    JOptionPane.INFORMATION_MESSAGE);
3680            return;
3681        }
3682        LayoutBlock block = track.getLayoutBlock();
3683        if (block == null) {
3684            JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3685                    Bundle.getMessage("InfoMessage6"),
3686                    Bundle.getMessage("MessageTitle"),
3687                    JOptionPane.INFORMATION_MESSAGE);
3688            return;
3689        }
3690        Sensor occupancy = block.getOccupancySensor();
3691        if (occupancy == null) {
3692            JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3693                    Bundle.getMessage("InfoMessage4",
3694                            new Object[]{block.getUserName()}),
3695                    Bundle.getMessage("MessageTitle"),
3696                    JOptionPane.INFORMATION_MESSAGE);
3697            return;
3698        }
3699        SignalHead nextHead = getNextSignalFromObject(track,
3700                layoutTurnout, head.getSystemName(), setSignalsAtXoverTurnoutFrame);
3701        if ((nextHead == null) && (!reachedEndBumper())) {
3702            JOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3703                    Bundle.getMessage("InfoMessage5",
3704                            new Object[]{block.getUserName()}),
3705                    Bundle.getMessage("MessageTitle"),
3706                    JOptionPane.INFORMATION_MESSAGE);
3707            return;
3708        }
3709        if (!initializeBlockBossLogic(head.getDisplayName())) {
3710            return;
3711        }
3712        logic.setMode(BlockBossLogic.TRAILINGMAIN);
3713        logic.setTurnout(turnout.getDisplayName());
3714        logic.setSensor1(occupancy.getDisplayName());
3715        if (nextHead != null) {
3716            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
3717        }
3718        if (auxSignal != null) {
3719            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
3720        }
3721        finalizeBlockBossLogic();
3722    }   //setLogicXoverContinuing
3723
3724    /*=======================*\
3725    |* setSignalsAtLevelXing *|
3726    \*=======================*/
3727    /**
3728     * Tool to set signals at a level crossing, including placing the signal
3729     * icons and setup of Simple Signal Logic for each signal head
3730     * <p>
3731     * This tool assumes left facing signal head icons have been selected, and
3732     * will rotate the signal head icons accordingly.
3733     * <p>
3734     * This tool will place icons on the right side of each track.
3735     * <p>
3736     * Both tracks do not need to be signalled. If one signal for a track, A-C
3737     * or B-D, the other must also be present.
3738     * <p>
3739     * Some user adjustment of turnout positions may be needed.
3740     */
3741    //operational variables for Set Signals at Level Crossing tool
3742    private JmriJFrame setSignalsAtLevelXingFrame = null;
3743    private boolean setSignalsAtLevelXingOpenFlag = false;
3744    private boolean setSignalsAtLevelXingFromMenuFlag = false;
3745
3746    private JLabel blockACNameLabel = null;
3747    private JLabel blockBDNameLabel = null;
3748
3749    private final NamedBeanComboBox<Block> blockACComboBox = new NamedBeanComboBox<>(
3750            InstanceManager.getDefault(BlockManager.class),
3751            null, DisplayOptions.DISPLAYNAME);
3752    private final NamedBeanComboBox<Block> blockBDComboBox = new NamedBeanComboBox<>(
3753            InstanceManager.getDefault(BlockManager.class),
3754            null, DisplayOptions.DISPLAYNAME);
3755
3756    private final NamedBeanComboBox<SignalHead> aSignalHeadComboBox = new NamedBeanComboBox<>(
3757            InstanceManager.getDefault(SignalHeadManager.class),
3758            null, DisplayOptions.DISPLAYNAME);
3759    private final NamedBeanComboBox<SignalHead> bSignalHeadComboBox = new NamedBeanComboBox<>(
3760            InstanceManager.getDefault(SignalHeadManager.class),
3761            null, DisplayOptions.DISPLAYNAME);
3762    private final NamedBeanComboBox<SignalHead> cSignalHeadComboBox = new NamedBeanComboBox<>(
3763            InstanceManager.getDefault(SignalHeadManager.class),
3764            null, DisplayOptions.DISPLAYNAME);
3765    private final NamedBeanComboBox<SignalHead> dSignalHeadComboBox = new NamedBeanComboBox<>(
3766            InstanceManager.getDefault(SignalHeadManager.class),
3767            null, DisplayOptions.DISPLAYNAME);
3768
3769    private final JCheckBox setAHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
3770    private final JCheckBox setBHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
3771    private final JCheckBox setCHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
3772    private final JCheckBox setDHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
3773
3774    private final JCheckBox setupALogic = new JCheckBox(Bundle.getMessage("SetLogic"));
3775    private final JCheckBox setupBLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
3776    private final JCheckBox setupCLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
3777    private final JCheckBox setupDLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
3778
3779    private JButton getSavedXingSignalHeads = null;
3780    private JButton changeXingSignalIcon = null;
3781    private JButton setXingSignalsDone = null;
3782    private JButton setXingSignalsCancel = null;
3783
3784    private LevelXing levelXing = null;
3785
3786    private SignalHead aHead = null;
3787    private SignalHead bHead = null;
3788    private SignalHead cHead = null;
3789    private SignalHead dHead = null;
3790
3791    //display dialog for Set Signals at Level Crossing tool
3792    public void setSignalsAtLevelXingFromMenu(@Nonnull LevelXing xing,
3793            @Nonnull MultiIconEditor theEditor,
3794            @Nonnull JFrame theFrame) {
3795        levelXing = xing;
3796        blockACComboBox.setSelectedItem(levelXing.getLayoutBlockAC().getBlock());
3797        blockBDComboBox.setSelectedItem(levelXing.getLayoutBlockBD().getBlock());
3798        setSignalsAtLevelXingFromMenuFlag = true;
3799        setSignalsAtLevelXing(theEditor, theFrame);
3800        setSignalsAtLevelXingFromMenuFlag = false;
3801    }
3802
3803    public void setSignalsAtLevelXing(@Nonnull MultiIconEditor theEditor,
3804            @Nonnull JFrame theFrame) {
3805        signalIconEditor = theEditor;
3806        signalFrame = theFrame;
3807
3808        //Initialize if needed
3809        if (setSignalsAtLevelXingFrame == null) {
3810            setSignalsAtLevelXingOpenFlag = false;
3811            setSignalsAtLevelXingFrame = new JmriJFrame(Bundle.getMessage("SignalsAtLevelXing"), false, true);
3812            oneFrameToRuleThemAll(setSignalsAtLevelXingFrame);
3813            setSignalsAtLevelXingFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
3814            setSignalsAtLevelXingFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtLevelXing", true);
3815            setSignalsAtLevelXingFrame.setLocation(70, 30);
3816            Container theContentPane = setSignalsAtLevelXingFrame.getContentPane();
3817            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
3818
3819            JPanel panel11 = new JPanel(new FlowLayout());
3820            blockACNameLabel = new JLabel(Bundle.getMessage("MakeLabel", (Bundle.getMessage("BeanNameBlock") + " AC")));
3821            panel11.add(blockACNameLabel);
3822            panel11.add(blockACComboBox);
3823            blockACComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
3824            theContentPane.add(panel11);
3825
3826            JPanel panel12 = new JPanel(new FlowLayout());
3827            blockBDNameLabel = new JLabel(Bundle.getMessage("MakeLabel", (Bundle.getMessage("BeanNameBlock") + " BD")));
3828            panel12.add(blockBDNameLabel);
3829            panel12.add(blockBDComboBox);
3830            blockBDComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
3831
3832            theContentPane.add(panel12);
3833            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
3834
3835            JPanel panel2 = new JPanel(new FlowLayout());
3836            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
3837            panel2.add(shTitle);
3838            panel2.add(new JLabel("   "));
3839            panel2.add(getSavedXingSignalHeads = new JButton(Bundle.getMessage("GetSaved")));
3840            getSavedXingSignalHeads.addActionListener(this::xingSignalsGetSaved);
3841            getSavedXingSignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
3842            theContentPane.add(panel2);
3843
3844            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
3845            JPanel panel2a = new JPanel(new FlowLayout());
3846            panel2a.add(new JLabel("   "));
3847            panel2a.add(setPlaceAllHeads);
3848            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
3849            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
3850                boolean isSelected = setPlaceAllHeads.isSelected();
3851                //(de)select all checkboxes
3852                setAHead.setSelected(isSelected);
3853                setBHead.setSelected(isSelected);
3854                setCHead.setSelected(isSelected);
3855                setDHead.setSelected(isSelected);
3856            });
3857            panel2a.add(new JLabel("  "));
3858            panel2a.add(setupAllLogic);
3859            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
3860            setupAllLogic.addActionListener((ActionEvent e) -> {
3861                boolean isSelected = setupAllLogic.isSelected();
3862                //(de)select all checkboxes
3863                setupALogic.setSelected(isSelected);
3864                setupBLogic.setSelected(isSelected);
3865                setupCLogic.setSelected(isSelected);
3866                setupDLogic.setSelected(isSelected);
3867            });
3868            theContentPane.add(panel2a);
3869
3870            JPanel panel21 = new JPanel(new FlowLayout());
3871            JLabel aLabel = new JLabel(Bundle.getMessage("MakeLabel",
3872                    Bundle.getMessage("TrackXConnect", "A")));
3873            panel21.add(aLabel);
3874            panel21.add(aSignalHeadComboBox);
3875            theContentPane.add(panel21);
3876            aSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
3877
3878            JPanel panel22 = new JPanel(new FlowLayout());
3879            panel22.add(new JLabel("   "));
3880            panel22.add(setAHead);
3881            setAHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
3882            panel22.add(new JLabel("  "));
3883            panel22.add(setupALogic);
3884            setupALogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
3885            theContentPane.add(panel22);
3886
3887            JPanel panel31 = new JPanel(new FlowLayout());
3888            JLabel bLabel = new JLabel(Bundle.getMessage("MakeLabel",
3889                    Bundle.getMessage("TrackXConnect", "B")));
3890            panel31.add(bLabel);
3891            panel31.add(bSignalHeadComboBox);
3892            theContentPane.add(panel31);
3893            bSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
3894
3895            JPanel panel32 = new JPanel(new FlowLayout());
3896            panel32.add(new JLabel("   "));
3897            panel32.add(setBHead);
3898            setBHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
3899            panel32.add(new JLabel("  "));
3900            panel32.add(setupBLogic);
3901            setupBLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
3902            theContentPane.add(panel32);
3903
3904            JPanel panel41 = new JPanel(new FlowLayout());
3905            JLabel cLabel = new JLabel(Bundle.getMessage("MakeLabel",
3906                    Bundle.getMessage("TrackXConnect", "C")));
3907            panel41.add(cLabel);
3908            panel41.add(cSignalHeadComboBox);
3909            theContentPane.add(panel41);
3910            cSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
3911
3912            JPanel panel42 = new JPanel(new FlowLayout());
3913            panel42.add(new JLabel("   "));
3914            panel42.add(setCHead);
3915            setCHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
3916            panel42.add(new JLabel("  "));
3917            panel42.add(setupCLogic);
3918            setupCLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
3919            theContentPane.add(panel42);
3920
3921            JPanel panel51 = new JPanel(new FlowLayout());
3922            JLabel dLabel = new JLabel(Bundle.getMessage("MakeLabel",
3923                    Bundle.getMessage("TrackXConnect", "D")));
3924            panel51.add(dLabel);
3925            panel51.add(dSignalHeadComboBox);
3926            theContentPane.add(panel51);
3927            dSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
3928
3929            JPanel panel52 = new JPanel(new FlowLayout());
3930            panel52.add(new JLabel("   "));
3931            panel52.add(setDHead);
3932            setDHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
3933            panel52.add(new JLabel("  "));
3934            panel52.add(setupDLogic);
3935            setupDLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
3936            theContentPane.add(panel52);
3937            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
3938
3939            JPanel panel6 = new JPanel(new FlowLayout());
3940            panel6.add(changeXingSignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
3941            changeXingSignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
3942            changeXingSignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
3943            panel6.add(new JLabel("   "));
3944            panel6.add(setXingSignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
3945            setXingSignalsDone.addActionListener(this::setXingSignalsDonePressed);
3946            setXingSignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
3947
3948            panel6.add(setXingSignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
3949            setXingSignalsCancel.addActionListener(this::setXingSignalsCancelPressed);
3950            setXingSignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
3951            theContentPane.add(panel6);
3952
3953            //make this button the default button (return or enter activates)
3954            JRootPane rootPane = SwingUtilities.getRootPane(setXingSignalsDone);
3955            if (rootPane != null) {
3956                rootPane.setDefaultButton(setXingSignalsDone);
3957            }
3958
3959            setSignalsAtLevelXingFrame.addWindowListener(new WindowAdapter() {
3960                @Override
3961                public void windowClosing(WindowEvent e) {
3962                    setXingSignalsCancelPressed(null);
3963                }
3964            });
3965        }
3966
3967        aSignalHeadComboBox.setSelectedItem(null);
3968        bSignalHeadComboBox.setSelectedItem(null);
3969        cSignalHeadComboBox.setSelectedItem(null);
3970        dSignalHeadComboBox.setSelectedItem(null);
3971
3972        setPlaceAllHeads.setSelected(false);
3973        setupAllLogic.setSelected(false);
3974
3975        blockACComboBox.setVisible(!setSignalsAtLevelXingFromMenuFlag);
3976        blockBDComboBox.setVisible(!setSignalsAtLevelXingFromMenuFlag);
3977
3978        if (setSignalsAtLevelXingFromMenuFlag) {
3979            blockACNameLabel.setText(Bundle.getMessage("MakeLabel",
3980                    (Bundle.getMessage("BeanNameBlock") + " AC"))
3981                    + levelXing.getBlockNameAC());
3982            blockBDNameLabel.setText(Bundle.getMessage("MakeLabel",
3983                    (Bundle.getMessage("BeanNameBlock") + " BD"))
3984                    + levelXing.getBlockNameBD());
3985            xingSignalsGetSaved(null);
3986        } else {
3987            blockACNameLabel.setText(Bundle.getMessage("MakeLabel", (Bundle.getMessage("BeanNameBlock") + " AC")));
3988            blockBDNameLabel.setText(Bundle.getMessage("MakeLabel", (Bundle.getMessage("BeanNameBlock") + " BD")));
3989        }
3990
3991        if (!setSignalsAtLevelXingOpenFlag) {
3992            setSignalsAtLevelXingFrame.setPreferredSize(null);
3993            setSignalsAtLevelXingFrame.pack();
3994            setSignalsAtLevelXingOpenFlag = true;
3995        }
3996
3997        setSignalsAtLevelXingFrame.setVisible(true);
3998    }   //setSignalsAtLevelXing
3999
4000    private void xingSignalsGetSaved(ActionEvent a) {
4001        if (!getLevelCrossingInformation()) {
4002            return;
4003        }
4004        aSignalHeadComboBox.setSelectedItem(levelXing.getSignalHead(LevelXing.Geometry.POINTA));
4005        bSignalHeadComboBox.setSelectedItem(levelXing.getSignalHead(LevelXing.Geometry.POINTB));
4006        cSignalHeadComboBox.setSelectedItem(levelXing.getSignalHead(LevelXing.Geometry.POINTC));
4007        dSignalHeadComboBox.setSelectedItem(levelXing.getSignalHead(LevelXing.Geometry.POINTD));
4008    }
4009
4010    private void setXingSignalsCancelPressed(ActionEvent a) {
4011        setSignalsAtLevelXingOpenFlag = false;
4012        setSignalsAtLevelXingFrame.setVisible(false);
4013    }
4014
4015    private void setXingSignalsDonePressed(ActionEvent a) {
4016        if (!getLevelCrossingInformation()) {
4017            return;
4018        }
4019        if (!getXingSignalHeadInformation()) {
4020            return;
4021        }
4022
4023        //place or update signals as requested
4024        String signalName = aSignalHeadComboBox.getSelectedItemDisplayName();
4025        if (signalName == null) {
4026            signalName = "";
4027        }
4028        if ((aHead != null) && setAHead.isSelected()) {
4029            if (isHeadOnPanel(aHead)
4030                    && (aHead != getHeadFromName(levelXing.getSignalAName()))) {
4031                JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4032                        Bundle.getMessage("SignalsError6",
4033                                new Object[]{signalName}),
4034                        Bundle.getMessage("ErrorTitle"),
4035                        JOptionPane.ERROR_MESSAGE);
4036                return;
4037            } else {
4038                removeSignalHeadFromPanel(levelXing.getSignalAName());
4039                placeXingA();
4040                removeAssignment(aHead);
4041                levelXing.setSignalAName(signalName);
4042                needRedraw = true;
4043            }
4044        } else if ((aHead != null)
4045                && (aHead != getHeadFromName(levelXing.getSignalAName()))
4046                && (aHead != getHeadFromName(levelXing.getSignalBName()))
4047                && (aHead != getHeadFromName(levelXing.getSignalCName()))
4048                && (aHead != getHeadFromName(levelXing.getSignalDName()))) {
4049            if (isHeadOnPanel(aHead)) {
4050                JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4051                        Bundle.getMessage("SignalsError13",
4052                                new Object[]{signalName}),
4053                        Bundle.getMessage("ErrorTitle"),
4054                        JOptionPane.ERROR_MESSAGE);
4055                return;
4056            } else {
4057                removeSignalHeadFromPanel(levelXing.getSignalAName());
4058                removeAssignment(aHead);
4059                levelXing.setSignalAName(signalName);
4060            }
4061        } else if ((aHead != null)
4062                && ((aHead == getHeadFromName(levelXing.getSignalBName()))
4063                || (aHead == getHeadFromName(levelXing.getSignalCName()))
4064                || (aHead == getHeadFromName(levelXing.getSignalDName())))) {
4065            //need to figure out what to do in this case.
4066            log.trace("need to figure out what to do in this case.");
4067        } else if (aHead == null) {
4068            removeSignalHeadFromPanel(levelXing.getSignalAName());
4069            levelXing.setSignalAName("");
4070        }
4071        signalName = bSignalHeadComboBox.getSelectedItemDisplayName();
4072        if (signalName == null) {
4073            signalName = "";
4074        }
4075        if ((bHead != null) && setBHead.isSelected()) {
4076            if (isHeadOnPanel(bHead)
4077                    && (bHead != getHeadFromName(levelXing.getSignalBName()))) {
4078                JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4079                        Bundle.getMessage("SignalsError6",
4080                                new Object[]{signalName}),
4081                        Bundle.getMessage("ErrorTitle"),
4082                        JOptionPane.ERROR_MESSAGE);
4083                return;
4084            } else {
4085                removeSignalHeadFromPanel(levelXing.getSignalBName());
4086                placeXingB();
4087                removeAssignment(bHead);
4088                levelXing.setSignalBName(signalName);
4089                needRedraw = true;
4090            }
4091        } else if ((bHead != null)
4092                && (bHead != getHeadFromName(levelXing.getSignalAName()))
4093                && (bHead != getHeadFromName(levelXing.getSignalBName()))
4094                && (bHead != getHeadFromName(levelXing.getSignalCName()))
4095                && (bHead != getHeadFromName(levelXing.getSignalDName()))) {
4096            if (isHeadOnPanel(bHead)) {
4097                JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4098                        Bundle.getMessage("SignalsError13",
4099                                new Object[]{signalName}),
4100                        Bundle.getMessage("ErrorTitle"),
4101                        JOptionPane.ERROR_MESSAGE);
4102                return;
4103            } else {
4104                removeSignalHeadFromPanel(levelXing.getSignalBName());
4105                removeAssignment(bHead);
4106                levelXing.setSignalBName(signalName);
4107            }
4108        } else if ((bHead != null)
4109                && ((bHead == getHeadFromName(levelXing.getSignalAName()))
4110                || (bHead == getHeadFromName(levelXing.getSignalCName()))
4111                || (bHead == getHeadFromName(levelXing.getSignalDName())))) {
4112            //need to figure out what to do in this case.
4113            log.trace("need to figure out what to do in this case.");
4114        } else if (bHead == null) {
4115            removeSignalHeadFromPanel(levelXing.getSignalBName());
4116            levelXing.setSignalBName("");
4117        }
4118        signalName = cSignalHeadComboBox.getSelectedItemDisplayName();
4119        if (signalName == null) {
4120            signalName = "";
4121        }
4122        if ((cHead != null) && setCHead.isSelected()) {
4123            if (isHeadOnPanel(cHead)
4124                    && (cHead != getHeadFromName(levelXing.getSignalCName()))) {
4125                JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4126                        Bundle.getMessage("SignalsError6",
4127                                new Object[]{signalName}),
4128                        Bundle.getMessage("ErrorTitle"),
4129                        JOptionPane.ERROR_MESSAGE);
4130                return;
4131            } else {
4132                removeSignalHeadFromPanel(levelXing.getSignalCName());
4133                placeXingC();
4134                removeAssignment(cHead);
4135                levelXing.setSignalCName(signalName);
4136                needRedraw = true;
4137            }
4138        } else if ((cHead != null)
4139                && (cHead != getHeadFromName(levelXing.getSignalAName()))
4140                && (cHead != getHeadFromName(levelXing.getSignalBName()))
4141                && (cHead != getHeadFromName(levelXing.getSignalCName()))
4142                && (cHead != getHeadFromName(levelXing.getSignalDName()))) {
4143            if (isHeadOnPanel(cHead)) {
4144                JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4145                        Bundle.getMessage("SignalsError13",
4146                                new Object[]{signalName}),
4147                        Bundle.getMessage("ErrorTitle"),
4148                        JOptionPane.ERROR_MESSAGE);
4149                return;
4150            } else {
4151                removeSignalHeadFromPanel(levelXing.getSignalCName());
4152                removeAssignment(cHead);
4153                levelXing.setSignalCName(signalName);
4154            }
4155        } else if ((cHead != null)
4156                && ((cHead == getHeadFromName(levelXing.getSignalBName()))
4157                || (cHead == getHeadFromName(levelXing.getSignalAName()))
4158                || (cHead == getHeadFromName(levelXing.getSignalDName())))) {
4159            //need to figure out what to do in this case.
4160            log.trace("need to figure out what to do in this case.");
4161        } else if (cHead == null) {
4162            removeSignalHeadFromPanel(levelXing.getSignalCName());
4163            levelXing.setSignalCName("");
4164        }
4165        signalName = dSignalHeadComboBox.getSelectedItemDisplayName();
4166        if (signalName == null) {
4167            signalName = "";
4168        }
4169        if ((dHead != null) && setDHead.isSelected()) {
4170            if (isHeadOnPanel(dHead)
4171                    && (dHead != getHeadFromName(levelXing.getSignalDName()))) {
4172                JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4173                        Bundle.getMessage("SignalsError6",
4174                                new Object[]{signalName}),
4175                        Bundle.getMessage("ErrorTitle"),
4176                        JOptionPane.ERROR_MESSAGE);
4177                return;
4178            } else {
4179                removeSignalHeadFromPanel(levelXing.getSignalDName());
4180                placeXingD();
4181                removeAssignment(dHead);
4182                levelXing.setSignalDName(signalName);
4183                needRedraw = true;
4184            }
4185        } else if ((dHead != null)
4186                && (dHead != getHeadFromName(levelXing.getSignalAName()))
4187                && (dHead != getHeadFromName(levelXing.getSignalBName()))
4188                && (dHead != getHeadFromName(levelXing.getSignalCName()))
4189                && (dHead != getHeadFromName(levelXing.getSignalDName()))) {
4190            if (isHeadOnPanel(dHead)) {
4191                JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4192                        Bundle.getMessage("SignalsError13",
4193                                new Object[]{signalName}),
4194                        Bundle.getMessage("ErrorTitle"),
4195                        JOptionPane.ERROR_MESSAGE);
4196                return;
4197            } else {
4198                removeSignalHeadFromPanel(levelXing.getSignalDName());
4199                removeAssignment(dHead);
4200                levelXing.setSignalDName(signalName);
4201            }
4202        } else if ((dHead != null)
4203                && ((dHead == getHeadFromName(levelXing.getSignalBName()))
4204                || (dHead == getHeadFromName(levelXing.getSignalCName()))
4205                || (dHead == getHeadFromName(levelXing.getSignalAName())))) {
4206            //need to figure out what to do in this case.
4207            log.trace("need to figure out what to do in this case.");
4208        } else if (dHead == null) {
4209            removeSignalHeadFromPanel(levelXing.getSignalDName());
4210            levelXing.setSignalDName("");
4211        }
4212        //setup logic if requested
4213        if (setupALogic.isSelected() && (aHead != null)) {
4214            setLogicXing(aHead, (TrackSegment) levelXing.getConnectC(),
4215                    levelXing.getLayoutBlockBD(), (TrackSegment) levelXing.getConnectB(),
4216                    (TrackSegment) levelXing.getConnectD(), aSignalHeadComboBox.getSelectedItemDisplayName());
4217        }
4218        if (setupBLogic.isSelected() && (bHead != null)) {
4219            setLogicXing(bHead, (TrackSegment) levelXing.getConnectD(),
4220                    levelXing.getLayoutBlockAC(), (TrackSegment) levelXing.getConnectA(),
4221                    (TrackSegment) levelXing.getConnectC(), bSignalHeadComboBox.getSelectedItemDisplayName());
4222        }
4223        if (setupCLogic.isSelected() && (cHead != null)) {
4224            setLogicXing(cHead, (TrackSegment) levelXing.getConnectA(),
4225                    levelXing.getLayoutBlockBD(), (TrackSegment) levelXing.getConnectB(),
4226                    (TrackSegment) levelXing.getConnectD(), cSignalHeadComboBox.getSelectedItemDisplayName());
4227        }
4228        if (setupDLogic.isSelected() && (dHead != null)) {
4229            setLogicXing(dHead, (TrackSegment) levelXing.getConnectB(),
4230                    levelXing.getLayoutBlockAC(), (TrackSegment) levelXing.getConnectA(),
4231                    (TrackSegment) levelXing.getConnectC(), dSignalHeadComboBox.getSelectedItemDisplayName());
4232        }
4233        //finish up
4234        setSignalsAtLevelXingOpenFlag = false;
4235        setSignalsAtLevelXingFrame.setVisible(false);
4236        if (needRedraw) {
4237            layoutEditor.redrawPanel();
4238            needRedraw = false;
4239            layoutEditor.setDirty();
4240        }
4241    }   //setXingSignalsDonePressed
4242
4243    private boolean getLevelCrossingInformation() {
4244        if (!setSignalsAtLevelXingFromMenuFlag) {
4245            levelXing = null;
4246            List<LevelXing> levelXings = layoutEditor.getLevelXings();
4247            if (levelXings.size() <= 0) {
4248                JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4249                        Bundle.getMessage("SignalsError15"),
4250                        Bundle.getMessage("ErrorTitle"),
4251                        JOptionPane.ERROR_MESSAGE);
4252                return false;
4253            } else if (levelXings.size() == 1) {
4254                levelXing = levelXings.get(0);
4255            } else {
4256                LayoutBlock xingBlockA = null;
4257                xingBlockA = getBlockFromEntry(blockACComboBox);
4258                if (xingBlockA == null) {
4259                    return false;
4260                }
4261
4262                LayoutBlock xingBlockC = getBlockFromEntry(blockBDComboBox);
4263                if (xingBlockC == null) {
4264                    return false;
4265                }
4266
4267                int foundCount = 0;
4268                //make two block tests first
4269                for (LevelXing x : layoutEditor.getLevelXings()) {
4270                    LayoutBlock xA = null;
4271                    LayoutBlock xB = null;
4272                    LayoutBlock xC = null;
4273                    LayoutBlock xD = null;
4274                    LayoutBlock xAC = x.getLayoutBlockAC();
4275                    LayoutBlock xBD = x.getLayoutBlockBD();
4276                    if (x.getConnectA() != null) {
4277                        xA = ((TrackSegment) x.getConnectA()).getLayoutBlock();
4278                    }
4279                    if (x.getConnectB() != null) {
4280                        xB = ((TrackSegment) x.getConnectB()).getLayoutBlock();
4281                    }
4282                    if (x.getConnectC() != null) {
4283                        xC = ((TrackSegment) x.getConnectC()).getLayoutBlock();
4284                    }
4285                    if (x.getConnectD() != null) {
4286                        xD = ((TrackSegment) x.getConnectD()).getLayoutBlock();
4287                    }
4288                    if (((xA != null) && (xC != null) && (((xA == xingBlockA) && (xC == xingBlockC))
4289                            || ((xA == xingBlockC) && (xC == xingBlockA))))
4290                            || ((xB != null) && (xD != null) && (((xB == xingBlockA) && (xD == xingBlockC))
4291                            || ((xB == xingBlockC) && (xD == xingBlockA))))) {
4292                        levelXing = x;
4293                        foundCount++;
4294                    } else if ((xAC != null) && (xBD != null) && (((xAC == xingBlockA) && (xBD == xingBlockC))
4295                            || ((xAC == xingBlockC) && (xBD == xingBlockA)))) {
4296                        levelXing = x;
4297                        foundCount++;
4298                    }
4299                }
4300                if (foundCount == 0) {
4301                    //try one block test
4302                    for (LevelXing x : layoutEditor.getLevelXings()) {
4303                        if ((xingBlockA == x.getLayoutBlockAC()) || (xingBlockA == x.getLayoutBlockBD())) {
4304                            levelXing = x;
4305                            foundCount++;
4306                        }
4307                    }
4308                }
4309                if (foundCount > 1) {
4310                    JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4311                            Bundle.getMessage("SignalsError16",
4312                                    new Object[]{" " + foundCount + " "}),
4313                            Bundle.getMessage("ErrorTitle"),
4314                            JOptionPane.ERROR_MESSAGE);
4315                    return false;
4316                }
4317                if (levelXing == null) {
4318                    JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4319                            Bundle.getMessage("SignalsError17"),
4320                            Bundle.getMessage("ErrorTitle"),
4321                            JOptionPane.ERROR_MESSAGE);
4322                    return false;
4323                }
4324            }
4325        }
4326
4327        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
4328
4329        Point2D coordsA = levelXingView.getCoordsA();
4330        Point2D coordsC = levelXingView.getCoordsC();
4331        placeSignalDirectionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsA));
4332
4333        return true;
4334    }   //getLevelCrossingInformation
4335
4336    private boolean getXingSignalHeadInformation() {
4337        //note that all heads are optional, but pairs must be present
4338        aHead = getSignalHeadFromEntry(aSignalHeadComboBox, false, setSignalsAtLevelXingFrame);
4339        bHead = getSignalHeadFromEntry(bSignalHeadComboBox, false, setSignalsAtLevelXingFrame);
4340        cHead = getSignalHeadFromEntry(cSignalHeadComboBox, false, setSignalsAtLevelXingFrame);
4341        dHead = getSignalHeadFromEntry(dSignalHeadComboBox, false, setSignalsAtLevelXingFrame);
4342        if (((aHead != null) && (cHead == null)) || ((aHead == null) && (cHead != null))
4343                || ((bHead != null) && (dHead == null)) || ((bHead == null) && (dHead != null))) {
4344            JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4345                    Bundle.getMessage("SignalsError14"),
4346                    Bundle.getMessage("ErrorTitle"),
4347                    JOptionPane.ERROR_MESSAGE);
4348            return false;
4349        }
4350        if ((aHead == null) && (bHead == null) && (cHead == null) && (dHead == null)) {
4351            JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4352                    Bundle.getMessage("SignalsError12"),
4353                    Bundle.getMessage("ErrorTitle"),
4354                    JOptionPane.ERROR_MESSAGE);
4355            return false;
4356        }
4357        return true;
4358    }
4359
4360    private void placeXingA() {
4361        if (testIcon == null) {
4362            testIcon = signalIconEditor.getIcon(0);
4363        }
4364        String signalHeadName = aSignalHeadComboBox.getSelectedItemDisplayName();
4365        if (signalHeadName == null) {
4366            signalHeadName = "";
4367        }
4368        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
4369
4370        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
4371
4372        Point2D coordsA = levelXingView.getCoordsA();
4373        Point2D delta = new Point2D.Double(0.0, +shift);
4374
4375        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
4376        Point2D where = MathUtil.add(coordsA, delta);
4377        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
4378    }
4379
4380    private void placeXingB() {
4381        if (testIcon == null) {
4382            testIcon = signalIconEditor.getIcon(0);
4383        }
4384        String signalHeadName = bSignalHeadComboBox.getSelectedItemDisplayName();
4385        if (signalHeadName == null) {
4386            signalHeadName = "";
4387        }
4388        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
4389
4390        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
4391
4392        Point2D coordsB = levelXingView.getCoordsB();
4393        Point2D coordsD = levelXingView.getCoordsD();
4394
4395        double directionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsD));
4396        Point2D delta = new Point2D.Double(0.0, -shift);
4397
4398        delta = MathUtil.rotateDEG(delta, directionDEG);
4399        Point2D where = MathUtil.add(coordsB, delta);
4400        setSignalHeadOnPanel(directionDEG, signalHeadName, where);
4401    }
4402
4403    private void placeXingC() {
4404        if (testIcon == null) {
4405            testIcon = signalIconEditor.getIcon(0);
4406        }
4407        String signalHeadName = cSignalHeadComboBox.getSelectedItemDisplayName();
4408        if (signalHeadName == null) {
4409            signalHeadName = "";
4410        }
4411        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
4412
4413        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
4414
4415        Point2D coordsC = levelXingView.getCoordsC();
4416        Point2D delta = new Point2D.Double(0.0, -shift);
4417
4418        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
4419        Point2D where = MathUtil.add(coordsC, delta);
4420        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
4421    }
4422
4423    private void placeXingD() {
4424        if (testIcon == null) {
4425            testIcon = signalIconEditor.getIcon(0);
4426        }
4427        String signalHeadName = dSignalHeadComboBox.getSelectedItemDisplayName();
4428        if (signalHeadName == null) {
4429            signalHeadName = "";
4430        }
4431        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
4432
4433        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
4434
4435        Point2D coordsB = levelXingView.getCoordsB();
4436        Point2D coordsD = levelXingView.getCoordsD();
4437
4438        double directionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsD, coordsB));
4439        double diffDirDEG = MathUtil.diffAngleDEG(placeSignalDirectionDEG, directionDEG + 180.0);
4440        Point2D delta = new Point2D.Double(-shift * Math.cos(Math.toRadians(diffDirDEG)), -shift);
4441
4442        delta = MathUtil.rotateDEG(delta, directionDEG);
4443        Point2D where = MathUtil.add(coordsD, delta);
4444        setSignalHeadOnPanel(directionDEG, signalHeadName, where);
4445    }
4446
4447    @SuppressWarnings("null")
4448    private void setLogicXing(SignalHead head, TrackSegment track, LayoutBlock crossBlock,
4449            TrackSegment crossTrack1, TrackSegment crossTrack2, String signalHeadName) {
4450        if (track == null) {
4451            JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4452                    Bundle.getMessage("InfoMessage7"),
4453                    Bundle.getMessage("MessageTitle"),
4454                    JOptionPane.INFORMATION_MESSAGE);
4455            return;
4456        }
4457        Sensor occupancy = null;
4458        Sensor crossOccupancy = null;
4459        Sensor track1Occupancy = null;
4460        Sensor track2Occupancy = null;
4461        SignalHead nextHead = null;
4462        LayoutBlock block = track.getLayoutBlock();
4463        if (block == null) {
4464            JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4465                    Bundle.getMessage("InfoMessage6"),
4466                    Bundle.getMessage("MessageTitle"),
4467                    JOptionPane.INFORMATION_MESSAGE);
4468            return;
4469        }
4470        occupancy = block.getOccupancySensor();
4471        if (occupancy == null) {
4472            JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4473                    Bundle.getMessage("InfoMessage4",
4474                            new Object[]{block.getUserName()}),
4475                    Bundle.getMessage("MessageTitle"),
4476                    JOptionPane.INFORMATION_MESSAGE);
4477            return;
4478        }
4479        if (crossBlock != null) {
4480            crossOccupancy = crossBlock.getOccupancySensor();
4481        }
4482        if (crossTrack1 != null) {
4483            block = crossTrack1.getLayoutBlock();
4484            if (block != null) {
4485                track1Occupancy = block.getOccupancySensor();
4486                if (track1Occupancy == crossOccupancy) {
4487                    track1Occupancy = null;
4488                }
4489            }
4490        }
4491        if (crossTrack2 != null) {
4492            block = crossTrack2.getLayoutBlock();
4493            if (block != null) {
4494                track2Occupancy = block.getOccupancySensor();
4495                if ((track2Occupancy == crossOccupancy)
4496                        || (track2Occupancy == track1Occupancy)) {
4497                    track2Occupancy = null;
4498                }
4499            }
4500        }
4501        nextHead = getNextSignalFromObject(track, levelXing,
4502                head.getSystemName(), setSignalsAtXoverTurnoutFrame);
4503        if ((nextHead == null) && (!reachedEndBumper())) {
4504            JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4505                    Bundle.getMessage("InfoMessage5",
4506                            new Object[]{block.getUserName()}),
4507                    Bundle.getMessage("MessageTitle"),
4508                    JOptionPane.INFORMATION_MESSAGE);
4509            return;
4510        }
4511        if ((crossOccupancy == null) && (track1Occupancy == null) && (track2Occupancy == null)) {
4512            JOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4513                    Bundle.getMessage("SignalsWarn1",
4514                            new Object[]{signalHeadName}),
4515                    Bundle.getMessage("WarningTitle"),
4516                    JOptionPane.WARNING_MESSAGE);
4517        }
4518        if (!initializeBlockBossLogic(head.getDisplayName())) {
4519            return;
4520        }
4521        logic.setMode(BlockBossLogic.SINGLEBLOCK);
4522        logic.setSensor1(occupancy.getDisplayName());
4523        if (nextHead != null) {
4524            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
4525        }
4526        if (auxSignal != null) {
4527            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
4528        }
4529        if (crossOccupancy != null) {
4530            logic.setSensor2(crossOccupancy.getDisplayName());
4531            if (track1Occupancy != null) {
4532                logic.setSensor3(track1Occupancy.getDisplayName());
4533                if (track2Occupancy != null) {
4534                    logic.setSensor4(track2Occupancy.getDisplayName());
4535                }
4536            } else if (track2Occupancy != null) {
4537                logic.setSensor3(track2Occupancy.getDisplayName());
4538            }
4539        } else if (track1Occupancy != null) {
4540            logic.setSensor2(track1Occupancy.getDisplayName());
4541            if (track2Occupancy != null) {
4542                logic.setSensor3(track2Occupancy.getDisplayName());
4543            }
4544        } else if (track2Occupancy != null) {
4545            logic.setSensor2(track2Occupancy.getDisplayName());
4546        }
4547        finalizeBlockBossLogic();
4548    }
4549
4550    /*====================================*\
4551    |* setSignalsAtThroatToThroatTurnouts *|
4552    \*====================================*/
4553    /**
4554     * Tool to set signals at throat-to-throat turnouts, including placing the
4555     * signal icons and setup of signal logic for each signal head
4556     * <p>
4557     * This tool can only be accessed from the Tools menu. There is no access
4558     * from a turnout pop-up menu.
4559     * <p>
4560     * This tool requires a situation where two turnouts are connected throat-
4561     * to-throat by a single "short" track segment. The actual length of the
4562     * track segment is not tested. If this situation is not found, and error
4563     * message is sent to the user. To get started with this the user needs to
4564     * enter at least one of the two connected turnouts.
4565     * <p>
4566     * This tool assumes two turnouts connected throat-to-throat, as would be
4567     * used to represent a double slip turnout. The turnouts may be either
4568     * left-handed, right-handed, wye, or any pair of these. This tool also
4569     * assumes that there are no signals at the throat junction. The signal
4570     * heads will be rotated to face outward--away from the throats. Four sets
4571     * of one or two signal heads will be placed, one at each of the converging
4572     * and diverging for each turnout.
4573     * <p>
4574     * This tool assumes that each of the four tracks is contained in a
4575     * different block. Things work best if the two throat-to-throat turnouts
4576     * are in their own separate block, but this is not necessary.
4577     * <p>
4578     * This tool will place icons on the outside edges of each turnout.
4579     * <p>
4580     * At least one signal at each of the four connection points is required. A
4581     * second signal at each is optional.
4582     */
4583    //operational variables for Set Signals at Double Crossover Turnout tool
4584    private JmriJFrame setSignalsAtThroatToThroatTurnoutsFrame = null;
4585    private boolean setSignalsAtThroatToThroatTurnoutsOpenFlag = false;
4586    private boolean setSignalsAtThroatToThroatTurnoutsFromMenuFlag = false;
4587
4588    private JLabel ttotTurnoutName1Label = null;
4589    private JLabel ttotTurnoutName2Label = null;
4590
4591    private final NamedBeanComboBox<Turnout> turnout1ComboBox = new NamedBeanComboBox<>(
4592            InstanceManager.turnoutManagerInstance(),
4593            null, DisplayOptions.DISPLAYNAME);
4594    private final NamedBeanComboBox<Turnout> turnout2ComboBox = new NamedBeanComboBox<>(
4595            InstanceManager.turnoutManagerInstance(),
4596            null, DisplayOptions.DISPLAYNAME);
4597
4598    private final NamedBeanComboBox<SignalHead> a1TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4599            InstanceManager.getDefault(SignalHeadManager.class
4600            ),
4601            null, DisplayOptions.DISPLAYNAME);
4602    private final NamedBeanComboBox<SignalHead> a2TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4603            InstanceManager.getDefault(SignalHeadManager.class
4604            ),
4605            null, DisplayOptions.DISPLAYNAME);
4606    private final NamedBeanComboBox<SignalHead> b1TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4607            InstanceManager.getDefault(SignalHeadManager.class
4608            ),
4609            null, DisplayOptions.DISPLAYNAME);
4610    private final NamedBeanComboBox<SignalHead> b2TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4611            InstanceManager.getDefault(SignalHeadManager.class
4612            ),
4613            null, DisplayOptions.DISPLAYNAME);
4614    private final NamedBeanComboBox<SignalHead> c1TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4615            InstanceManager.getDefault(SignalHeadManager.class
4616            ),
4617            null, DisplayOptions.DISPLAYNAME);
4618    private final NamedBeanComboBox<SignalHead> c2TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4619            InstanceManager.getDefault(SignalHeadManager.class
4620            ),
4621            null, DisplayOptions.DISPLAYNAME);
4622    private final NamedBeanComboBox<SignalHead> d1TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4623            InstanceManager.getDefault(SignalHeadManager.class
4624            ),
4625            null, DisplayOptions.DISPLAYNAME);
4626    private final NamedBeanComboBox<SignalHead> d2TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4627            InstanceManager.getDefault(SignalHeadManager.class
4628            ),
4629            null, DisplayOptions.DISPLAYNAME);
4630
4631    private final JCheckBox setA1TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4632    private final JCheckBox setA2TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4633    private final JCheckBox setB1TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4634    private final JCheckBox setB2TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4635    private final JCheckBox setC1TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4636    private final JCheckBox setC2TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4637    private final JCheckBox setD1TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4638    private final JCheckBox setD2TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4639
4640    private final JCheckBox setupA1TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4641    private final JCheckBox setupA2TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4642    private final JCheckBox setupB1TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4643    private final JCheckBox setupB2TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4644    private final JCheckBox setupC1TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4645    private final JCheckBox setupC2TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4646    private final JCheckBox setupD1TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4647    private final JCheckBox setupD2TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4648
4649    private JButton getSavedTToTSignalHeads = null;
4650    private JButton changeTToTSignalIcon = null;
4651    private JButton setTToTSignalsDone = null;
4652    private JButton setTToTSignalsCancel = null;
4653
4654    private LayoutTurnout layoutTurnout1 = null;
4655    private LayoutTurnout layoutTurnout2 = null;
4656
4657    private Turnout turnout1 = null;
4658    private Turnout turnout2 = null;
4659
4660    private TrackSegment connectorTrack = null;
4661
4662    private String ttotTurnoutName1 = null;
4663    private String ttotTurnoutName2 = null;
4664
4665    private SignalHead a1TToTHead = null;
4666    private SignalHead a2TToTHead = null;
4667    private SignalHead b1TToTHead = null;
4668    private SignalHead b2TToTHead = null;
4669    private SignalHead c1TToTHead = null;
4670    private SignalHead c2TToTHead = null;
4671    private SignalHead d1TToTHead = null;
4672    private SignalHead d2TToTHead = null;
4673
4674    public void setSignalsAtThroatToThroatTurnoutsFromMenu(
4675            @Nonnull LayoutTurnout to, @Nonnull String linkedTurnoutName,
4676            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
4677        ttotTurnoutName1 = to.getTurnoutName();
4678        ttotTurnoutName2 = linkedTurnoutName;
4679
4680        turnout1ComboBox.setSelectedItem(to.getTurnout());
4681        turnout2ComboBox.setSelectedItem(to.getSecondTurnout());
4682
4683        a1TToTSignalHeadComboBox.setSelectedItem(null);
4684        a2TToTSignalHeadComboBox.setSelectedItem(null);
4685        b1TToTSignalHeadComboBox.setSelectedItem(null);
4686        b2TToTSignalHeadComboBox.setSelectedItem(null);
4687        c1TToTSignalHeadComboBox.setSelectedItem(null);
4688        c2TToTSignalHeadComboBox.setSelectedItem(null);
4689        d1TToTSignalHeadComboBox.setSelectedItem(null);
4690        d2TToTSignalHeadComboBox.setSelectedItem(null);
4691
4692        setSignalsAtThroatToThroatTurnoutsFromMenuFlag = true;
4693        setSignalsAtThroatToThroatTurnouts(theEditor, theFrame);
4694        setSignalsAtThroatToThroatTurnoutsFromMenuFlag = false;
4695    }
4696
4697    public void setSignalsAtThroatToThroatTurnouts(
4698            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
4699        signalIconEditor = theEditor;
4700        signalFrame = theFrame;
4701
4702        //Initialize if needed
4703        if (setSignalsAtThroatToThroatTurnoutsFrame == null) {
4704            setSignalsAtThroatToThroatTurnoutsOpenFlag = false;
4705            setSignalsAtThroatToThroatTurnoutsFrame = new JmriJFrame(Bundle.getMessage("SignalsAtTToTTurnout"), false, true);
4706            oneFrameToRuleThemAll(setSignalsAtThroatToThroatTurnoutsFrame);
4707            setSignalsAtThroatToThroatTurnoutsFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
4708            setSignalsAtThroatToThroatTurnoutsFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtTToTTurnout", true);
4709            setSignalsAtThroatToThroatTurnoutsFrame.setLocation(70, 30);
4710            Container theContentPane = setSignalsAtThroatToThroatTurnoutsFrame.getContentPane();
4711            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
4712
4713            JPanel panel1a = new JPanel(new FlowLayout());
4714            ttotTurnoutName1Label = new JLabel(Bundle.getMessage("BeanNameTurnout") + " 1 "
4715                    + Bundle.getMessage("Name"));
4716            panel1a.add(ttotTurnoutName1Label);
4717            panel1a.add(turnout1ComboBox);
4718            turnout1ComboBox.setToolTipText(Bundle.getMessage("SignalsTurnoutNameHint"));
4719            theContentPane.add(panel1a);
4720
4721            JPanel panel1b = new JPanel(new FlowLayout());
4722            ttotTurnoutName2Label = new JLabel(Bundle.getMessage("BeanNameTurnout") + " 2 "
4723                    + Bundle.getMessage("Name"));
4724            panel1b.add(ttotTurnoutName2Label);
4725            panel1b.add(turnout2ComboBox);
4726            turnout2ComboBox.setToolTipText(Bundle.getMessage("SignalsTurnoutNameHint"));
4727            theContentPane.add(panel1b);
4728            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
4729            //Provide for retrieval of names of previously saved signal heads
4730
4731            JPanel panel20 = new JPanel(new FlowLayout());
4732            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
4733            panel20.add(shTitle);
4734            panel20.add(new JLabel("   "));
4735            panel20.add(getSavedTToTSignalHeads = new JButton(Bundle.getMessage("GetSaved")));
4736            getSavedTToTSignalHeads.addActionListener(this::setSignalsAtTToTTurnoutsGetSaved);
4737            getSavedTToTSignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
4738            theContentPane.add(panel20);
4739            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
4740
4741            JPanel panel2a = new JPanel(new FlowLayout());
4742            panel2a.add(new JLabel("   "));
4743            panel2a.add(setPlaceAllHeads);
4744            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
4745            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
4746                boolean isSelected = setPlaceAllHeads.isSelected();
4747                //(de)select all checkboxes
4748                setA1TToTHead.setSelected(isSelected);
4749                setA2TToTHead.setSelected(isSelected);
4750                setB1TToTHead.setSelected(isSelected);
4751                setB2TToTHead.setSelected(isSelected);
4752                setC1TToTHead.setSelected(isSelected);
4753                setC2TToTHead.setSelected(isSelected);
4754                setD1TToTHead.setSelected(isSelected);
4755                setD2TToTHead.setSelected(isSelected);
4756            });
4757            panel2a.add(new JLabel("  "));
4758            panel2a.add(setupAllLogic);
4759            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
4760            setupAllLogic.addActionListener((ActionEvent e) -> {
4761                boolean isSelected = setupAllLogic.isSelected();
4762                //(de)select all checkboxes
4763                setupA1TToTLogic.setSelected(isSelected);
4764                setupA2TToTLogic.setSelected(isSelected);
4765                setupB1TToTLogic.setSelected(isSelected);
4766                setupB2TToTLogic.setSelected(isSelected);
4767                setupC1TToTLogic.setSelected(isSelected);
4768                setupC2TToTLogic.setSelected(isSelected);
4769                setupD1TToTLogic.setSelected(isSelected);
4770                setupD2TToTLogic.setSelected(isSelected);
4771            });
4772            theContentPane.add(panel2a);
4773            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
4774
4775            //Signal heads located at turnout 1
4776            JPanel panel20a = new JPanel(new FlowLayout());
4777            panel20a.add(new JLabel(Bundle.getMessage("SignalLocated")
4778                    + " " + Bundle.getMessage("BeanNameTurnout") + " 1 - "
4779                    + Bundle.getMessage("ContinuingTrack")));
4780            theContentPane.add(panel20a);
4781
4782            JPanel panel21 = new JPanel(new FlowLayout());
4783            panel21.add(new JLabel(Bundle.getMessage("MakeLabel",
4784                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
4785                    + Bundle.getMessage("ContinuingTrack"))));
4786            panel21.add(a1TToTSignalHeadComboBox);
4787            theContentPane.add(panel21);
4788            a1TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4789
4790            JPanel panel22 = new JPanel(new FlowLayout());
4791            panel22.add(new JLabel(Bundle.getMessage("OrBoth") + " 2 " + Bundle.getMessage("Tracks)") + "   "));
4792            panel22.add(setA1TToTHead);
4793            setA1TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4794            panel22.add(new JLabel("  "));
4795            panel22.add(setupA1TToTLogic);
4796            setupA1TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4797            theContentPane.add(panel22);
4798
4799            JPanel panel23 = new JPanel(new FlowLayout());
4800            panel23.add(new JLabel(Bundle.getMessage("MakeLabel",
4801                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
4802                    + Bundle.getMessage("DivergingTrack"))));
4803            panel23.add(a2TToTSignalHeadComboBox);
4804            theContentPane.add(panel23);
4805            a2TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4806
4807            JPanel panel24 = new JPanel(new FlowLayout());
4808            panel24.add(new JLabel("   "));
4809            panel24.add(setA2TToTHead);
4810            setA2TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4811            panel24.add(new JLabel("  "));
4812            panel24.add(setupA2TToTLogic);
4813            setupA2TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4814            theContentPane.add(panel24);
4815
4816            JPanel panel31x = new JPanel(new FlowLayout());
4817            panel31x.add(new JLabel(Bundle.getMessage("SignalLocated")
4818                    + " " + Bundle.getMessage("BeanNameTurnout") + " 1 - "
4819                    + Bundle.getMessage("DivergingTrack")));
4820            theContentPane.add(panel31x);
4821
4822            JPanel panel31 = new JPanel(new FlowLayout());
4823            panel31.add(new JLabel(Bundle.getMessage("MakeLabel",
4824                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
4825                    + Bundle.getMessage("ContinuingTrack"))));
4826            panel31.add(b1TToTSignalHeadComboBox);
4827            theContentPane.add(panel31);
4828            b1TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4829
4830            JPanel panel32 = new JPanel(new FlowLayout());
4831            panel32.add(new JLabel(Bundle.getMessage("OrBoth") + " 2 " + Bundle.getMessage("Tracks)") + "   "));
4832            panel32.add(setB1TToTHead);
4833            setB1TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4834            panel32.add(new JLabel("  "));
4835            panel32.add(setupB1TToTLogic);
4836            setupB1TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4837            theContentPane.add(panel32);
4838
4839            JPanel panel33 = new JPanel(new FlowLayout());
4840            panel33.add(new JLabel(Bundle.getMessage("MakeLabel",
4841                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
4842                    + Bundle.getMessage("DivergingTrack"))));
4843            panel33.add(b2TToTSignalHeadComboBox);
4844            theContentPane.add(panel33);
4845            b2TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4846
4847            JPanel panel34 = new JPanel(new FlowLayout());
4848            panel34.add(new JLabel("   "));
4849            panel34.add(setB2TToTHead);
4850            setB2TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4851            panel34.add(new JLabel("  "));
4852            panel34.add(setupB2TToTLogic);
4853            setupB2TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4854            theContentPane.add(panel34);
4855            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
4856            //Signal heads located at turnout 2
4857
4858            JPanel panel41x = new JPanel(new FlowLayout());
4859            panel41x.add(new JLabel(Bundle.getMessage("SignalLocated")
4860                    + " " + Bundle.getMessage("BeanNameTurnout") + " 2 - "
4861                    + Bundle.getMessage("ContinuingTrack")));
4862            theContentPane.add(panel41x);
4863
4864            JPanel panel41 = new JPanel(new FlowLayout());
4865            panel33.add(new JLabel(Bundle.getMessage("MakeLabel",
4866                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
4867                    + Bundle.getMessage("ContinuingTrack"))));
4868            panel41.add(c1TToTSignalHeadComboBox);
4869            theContentPane.add(panel41);
4870            c1TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4871
4872            JPanel panel42 = new JPanel(new FlowLayout());
4873            panel42.add(new JLabel(Bundle.getMessage("OrBoth") + " 1 " + Bundle.getMessage("Tracks)") + "   "));
4874            panel42.add(setC1TToTHead);
4875            setC1TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4876            panel42.add(new JLabel("  "));
4877            panel42.add(setupC1TToTLogic);
4878            setupC1TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4879            theContentPane.add(panel42);
4880
4881            JPanel panel43 = new JPanel(new FlowLayout());
4882            panel43.add(new JLabel(Bundle.getMessage("MakeLabel",
4883                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
4884                    + Bundle.getMessage("DivergingTrack"))));
4885            panel43.add(c2TToTSignalHeadComboBox);
4886            theContentPane.add(panel43);
4887            c2TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4888
4889            JPanel panel44 = new JPanel(new FlowLayout());
4890            panel44.add(new JLabel("   "));
4891            panel44.add(setC2TToTHead);
4892            setC2TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4893            panel44.add(new JLabel("  "));
4894            panel44.add(setupC2TToTLogic);
4895            setupC2TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4896            theContentPane.add(panel44);
4897
4898            JPanel panel51x = new JPanel(new FlowLayout());
4899            panel51x.add(new JLabel(Bundle.getMessage("SignalLocated")
4900                    + " " + Bundle.getMessage("BeanNameTurnout") + " 2 - "
4901                    + Bundle.getMessage("DivergingTrack")));
4902            theContentPane.add(panel51x);
4903
4904            JPanel panel51 = new JPanel(new FlowLayout());
4905            panel51.add(new JLabel(Bundle.getMessage("MakeLabel",
4906                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
4907                    + Bundle.getMessage("ContinuingTrack"))));
4908            panel51.add(d1TToTSignalHeadComboBox);
4909            theContentPane.add(panel51);
4910            d1TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4911
4912            JPanel panel52 = new JPanel(new FlowLayout());
4913            panel52.add(new JLabel(Bundle.getMessage("OrBoth") + " 1 " + Bundle.getMessage("Tracks)") + "   "));
4914            panel52.add(setD1TToTHead);
4915            setD1TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4916            panel52.add(new JLabel("  "));
4917            panel52.add(setupD1TToTLogic);
4918            setupD1TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4919            theContentPane.add(panel52);
4920
4921            JPanel panel53 = new JPanel(new FlowLayout());
4922            panel53.add(new JLabel(Bundle.getMessage("MakeLabel",
4923                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
4924                    + Bundle.getMessage("DivergingTrack"))));
4925            panel53.add(d2TToTSignalHeadComboBox);
4926            theContentPane.add(panel53);
4927            d2TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4928
4929            JPanel panel54 = new JPanel(new FlowLayout());
4930            panel54.add(new JLabel("   "));
4931            panel54.add(setD2TToTHead);
4932            setD2TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4933            panel54.add(new JLabel("  "));
4934            panel54.add(setupD2TToTLogic);
4935            setupD2TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4936            theContentPane.add(panel54);
4937            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
4938
4939            JPanel panel6 = new JPanel(new FlowLayout());
4940            panel6.add(changeTToTSignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
4941            changeTToTSignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
4942            changeTToTSignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
4943            panel6.add(new JLabel("   "));
4944            panel6.add(setTToTSignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
4945            setTToTSignalsDone.addActionListener(this::setTToTSignalsDonePressed);
4946            setTToTSignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
4947
4948            panel6.add(setTToTSignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
4949            setTToTSignalsCancel.addActionListener(this::setTToTSignalsCancelPressed);
4950            setTToTSignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
4951            theContentPane.add(panel6);
4952
4953            //make this button the default button (return or enter activates)
4954            JRootPane rootPane = SwingUtilities.getRootPane(setTToTSignalsDone);
4955            if (rootPane != null) {
4956                rootPane.setDefaultButton(setTToTSignalsDone);
4957            }
4958
4959            setSignalsAtThroatToThroatTurnoutsFrame.addWindowListener(new WindowAdapter() {
4960                @Override
4961                public void windowClosing(WindowEvent e) {
4962                    setTToTSignalsCancelPressed(null);
4963                }
4964            });
4965        }
4966        setPlaceAllHeads.setSelected(false);
4967        setupAllLogic.setSelected(false);
4968
4969        turnout1ComboBox.setVisible(!setSignalsAtThroatToThroatTurnoutsFromMenuFlag);
4970        turnout2ComboBox.setVisible(!setSignalsAtThroatToThroatTurnoutsFromMenuFlag);
4971
4972        if (setSignalsAtThroatToThroatTurnoutsFromMenuFlag) {
4973            ttotTurnoutName1Label.setText(Bundle.getMessage("MakeLabel",
4974                    Bundle.getMessage("BeanNameTurnout") + " 1 "
4975                    + Bundle.getMessage("Name")) + ttotTurnoutName1);
4976            ttotTurnoutName2Label.setText(Bundle.getMessage("MakeLabel",
4977                    Bundle.getMessage("BeanNameTurnout") + " 2 "
4978                    + Bundle.getMessage("Name")) + ttotTurnoutName2);
4979
4980            SwingUtilities.invokeLater(() -> setSignalsAtTToTTurnoutsGetSaved(null));
4981        } else {
4982            ttotTurnoutName1Label.setText(
4983                    Bundle.getMessage("BeanNameTurnout") + " 1 "
4984                    + Bundle.getMessage("Name"));
4985            ttotTurnoutName2Label.setText(
4986                    Bundle.getMessage("BeanNameTurnout") + " 2 "
4987                    + Bundle.getMessage("Name"));
4988        }
4989
4990        if (!setSignalsAtThroatToThroatTurnoutsOpenFlag) {
4991            setSignalsAtThroatToThroatTurnoutsFrame.setPreferredSize(null);
4992            setSignalsAtThroatToThroatTurnoutsFrame.pack();
4993            setSignalsAtThroatToThroatTurnoutsOpenFlag = true;
4994        }
4995        setSignalsAtThroatToThroatTurnoutsFrame.setVisible(true);
4996    }   //setSignalsAtTToTTurnouts
4997
4998    private void setSignalsAtTToTTurnoutsGetSaved(ActionEvent a) {
4999        if (!getTToTTurnoutInformation()) {
5000            return;
5001        }
5002        a1TToTSignalHeadComboBox.setSelectedItem(layoutTurnout1.getSignalB1());
5003        a2TToTSignalHeadComboBox.setSelectedItem(layoutTurnout1.getSignalB2());
5004        b1TToTSignalHeadComboBox.setSelectedItem(layoutTurnout1.getSignalC1());
5005        b2TToTSignalHeadComboBox.setSelectedItem(layoutTurnout1.getSignalC2());
5006        c1TToTSignalHeadComboBox.setSelectedItem(layoutTurnout2.getSignalB1());
5007        c2TToTSignalHeadComboBox.setSelectedItem(layoutTurnout2.getSignalB2());
5008        d1TToTSignalHeadComboBox.setSelectedItem(layoutTurnout2.getSignalC1());
5009        d2TToTSignalHeadComboBox.setSelectedItem(layoutTurnout2.getSignalC2());
5010    }
5011
5012    private void setTToTSignalsCancelPressed(ActionEvent a) {
5013        setSignalsAtThroatToThroatTurnoutsOpenFlag = false;
5014        setSignalsAtThroatToThroatTurnoutsFrame.setVisible(false);
5015    }
5016
5017    private boolean getTToTTurnoutInformation() {
5018        HitPointType type = HitPointType.NONE;
5019        Object connect = null;
5020
5021        turnout1 = null;
5022        turnout2 = null;
5023
5024        layoutTurnout1 = null;
5025        layoutTurnout2 = null;
5026
5027        if (!setSignalsAtThroatToThroatTurnoutsFromMenuFlag) {
5028            ttotTurnoutName1 = turnout1ComboBox.getSelectedItemDisplayName();
5029            if (ttotTurnoutName1 == null) {
5030                ttotTurnoutName1 = "";
5031            }
5032        }
5033        if (ttotTurnoutName1.isEmpty()) {
5034            //turnout 1 not entered, test turnout 2
5035            ttotTurnoutName2 = turnout2ComboBox.getSelectedItemDisplayName();
5036            if (ttotTurnoutName2 == null) {
5037                ttotTurnoutName2 = "";
5038            }
5039            if (ttotTurnoutName2.isEmpty()) {
5040                //no entries in turnout fields
5041                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5042                        Bundle.getMessage("SignalsError1"),
5043                        Bundle.getMessage("ErrorTitle"),
5044                        JOptionPane.ERROR_MESSAGE);
5045                return false;
5046            }
5047            turnout2 = InstanceManager.turnoutManagerInstance().getTurnout(ttotTurnoutName2);
5048            if (turnout2 == null) {
5049                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5050                        Bundle.getMessage("SignalsError2",
5051                                new Object[]{ttotTurnoutName2}), Bundle.getMessage("ErrorTitle"),
5052                        JOptionPane.ERROR_MESSAGE);
5053                return false;
5054            }
5055            String uname = turnout2.getUserName();
5056            if ((uname == null) || uname.isEmpty()
5057                    || !uname.equals(ttotTurnoutName2)) {
5058                turnout2ComboBox.setSelectedItem(turnout2);
5059            }
5060            layoutTurnout2 = getLayoutTurnoutFromTurnout(turnout2, false, ttotTurnoutName2, setSignalsAtThroatToThroatTurnoutsFrame);
5061            if (layoutTurnout2 == null) {
5062                return false;
5063            }
5064            //have turnout 2 and layout turnout 2 - look for turnout 1
5065            connectorTrack = (TrackSegment) layoutTurnout2.getConnectA();
5066            if (connectorTrack == null) {
5067                //Inform user of error, and terminate
5068                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5069                        Bundle.getMessage("SignalsError18"),
5070                        Bundle.getMessage("ErrorTitle"),
5071                        JOptionPane.ERROR_MESSAGE);
5072                return false;
5073            }
5074            type = connectorTrack.getType1();
5075            connect = connectorTrack.getConnect1();
5076            if (connect == layoutTurnout2) {
5077                type = connectorTrack.getType2();
5078                connect = connectorTrack.getConnect2();
5079            }
5080            if ((type != HitPointType.TURNOUT_A) || (connect == null)) {
5081                //Not two turnouts connected throat-to-throat by a single Track Segment
5082                //Inform user of error and terminate
5083                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5084                        Bundle.getMessage("SignalsError18"),
5085                        Bundle.getMessage("ErrorTitle"),
5086                        JOptionPane.ERROR_MESSAGE);
5087                return false;
5088            }
5089            layoutTurnout1 = (LayoutTurnout) connect;
5090            turnout1 = layoutTurnout1.getTurnout();
5091            if (turnout1 == null) {
5092                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5093                        Bundle.getMessage("SignalsError18"),
5094                        Bundle.getMessage("ErrorTitle"),
5095                        JOptionPane.ERROR_MESSAGE);
5096                return false;
5097            }
5098            turnout1ComboBox.setSelectedItem(turnout1);
5099        } else {
5100            //something was entered in the turnout 1 field
5101            turnout1 = InstanceManager.turnoutManagerInstance().getTurnout(ttotTurnoutName1);
5102            if (turnout1 == null) {
5103                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5104                        Bundle.getMessage("SignalsError2",
5105                                new Object[]{ttotTurnoutName1}), Bundle.getMessage("ErrorTitle"),
5106                        JOptionPane.ERROR_MESSAGE);
5107                return false;
5108            }
5109            String uname = turnout1.getUserName();
5110            if ((uname == null) || uname.isEmpty() || !uname.equals(ttotTurnoutName1)) {
5111                turnout1ComboBox.setSelectedItem(turnout1);
5112            }
5113            //have turnout 1 - get corresponding layoutTurnout
5114            layoutTurnout1 = getLayoutTurnoutFromTurnout(turnout1, false, ttotTurnoutName1, setSignalsAtThroatToThroatTurnoutsFrame);
5115            if (layoutTurnout1 == null) {
5116                return false;
5117            }
5118            turnout1ComboBox.setSelectedItem(layoutTurnout1.getTurnout());
5119            //have turnout 1 and layout turnout 1 - was something entered for turnout 2
5120            ttotTurnoutName2 = turnout2ComboBox.getSelectedItemDisplayName();
5121            if (ttotTurnoutName2 == null) {
5122                ttotTurnoutName2 = "";
5123            }
5124            if (ttotTurnoutName2.isEmpty()) {
5125                //no entry for turnout 2
5126                connectorTrack = (TrackSegment) layoutTurnout1.getConnectA();
5127                if (connectorTrack == null) {
5128                    //Inform user of error, and terminate
5129                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5130                            Bundle.getMessage("SignalsError18"),
5131                            Bundle.getMessage("ErrorTitle"),
5132                            JOptionPane.ERROR_MESSAGE);
5133                    return false;
5134                }
5135                type = connectorTrack.getType1();
5136                connect = connectorTrack.getConnect1();
5137                if (connect == layoutTurnout1) {
5138                    type = connectorTrack.getType2();
5139                    connect = connectorTrack.getConnect2();
5140                }
5141                if ((type != HitPointType.TURNOUT_A) || (connect == null)) {
5142                    //Not two turnouts connected throat-to-throat by a single Track Segment
5143                    //Inform user of error and terminate
5144                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5145                            Bundle.getMessage("SignalsError18"),
5146                            Bundle.getMessage("ErrorTitle"),
5147                            JOptionPane.ERROR_MESSAGE);
5148                    return false;
5149                }
5150                layoutTurnout2 = (LayoutTurnout) connect;
5151                turnout2 = layoutTurnout2.getTurnout();
5152                if (turnout2 == null) {
5153                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5154                            Bundle.getMessage("SignalsError18"),
5155                            Bundle.getMessage("ErrorTitle"),
5156                            JOptionPane.ERROR_MESSAGE);
5157                    return false;
5158                }
5159                turnout2ComboBox.setSelectedItem(turnout2);
5160            } else {
5161                //turnout 2 entered also
5162                turnout2 = InstanceManager.turnoutManagerInstance().getTurnout(ttotTurnoutName2);
5163                if (turnout2 == null) {
5164                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5165                            Bundle.getMessage("SignalsError2",
5166                                    new Object[]{ttotTurnoutName2}), Bundle.getMessage("ErrorTitle"),
5167                            JOptionPane.ERROR_MESSAGE);
5168                    return false;
5169                }
5170                uname = turnout2.getUserName();
5171                if ((uname == null) || uname.isEmpty() || !uname.equals(ttotTurnoutName2)) {
5172                    turnout2ComboBox.setSelectedItem(turnout2);
5173                }
5174                layoutTurnout2 = getLayoutTurnoutFromTurnout(turnout2, false, ttotTurnoutName2, setSignalsAtThroatToThroatTurnoutsFrame);
5175                if (layoutTurnout2 == null) {
5176                    return false;
5177                }
5178                turnout2ComboBox.setSelectedItem(layoutTurnout2.getTurnout());
5179                //check that layout turnout 1 and layout turnout 2 are connected throat-to-throat
5180                if (layoutTurnout1.getConnectA() != layoutTurnout2.getConnectA()) {
5181                    //Not two turnouts connected throat-to-throat by a single Track Segment
5182                    //Inform user of error and terminate
5183                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5184                            Bundle.getMessage("SignalsError18"),
5185                            Bundle.getMessage("ErrorTitle"),
5186                            JOptionPane.ERROR_MESSAGE);
5187                    return false;
5188                }
5189                connectorTrack = (TrackSegment) layoutTurnout1.getConnectA();
5190            }
5191        }
5192        //have both turnouts, correctly connected - complete initialization
5193        LayoutTurnoutView layoutTurnout1View = layoutEditor.getLayoutTurnoutView(layoutTurnout1);
5194        Point2D coordsA = layoutTurnout1View.getCoordsA();
5195        Point2D coordsCenter = layoutTurnout1View.getCoordsCenter();
5196        placeSignalDirectionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsCenter, coordsA));
5197        return true;
5198    }   //getTToTTurnoutInformation
5199
5200    private void setTToTSignalsDonePressed(ActionEvent a) {
5201        if (!getTToTTurnoutInformation()) {
5202            return;
5203        }
5204        if (!getTToTSignalHeadInformation()) {
5205            return;
5206        }
5207
5208        //place signal icons if requested, and assign signal heads to this turnout
5209        String signalHeadName = a1TToTSignalHeadComboBox.getSelectedItemDisplayName();
5210        if (signalHeadName == null) {
5211            signalHeadName = "";
5212        }
5213        if (setA1TToTHead.isSelected()) {
5214            if (isHeadOnPanel(a1TToTHead)
5215                    && (a1TToTHead != getHeadFromName(layoutTurnout1.getSignalB1Name()))) {
5216                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5217                        Bundle.getMessage("SignalsError6",
5218                                new Object[]{signalHeadName}),
5219                        Bundle.getMessage("ErrorTitle"),
5220                        JOptionPane.ERROR_MESSAGE);
5221                return;
5222            } else {
5223                removeSignalHeadFromPanel(layoutTurnout1.getSignalB1Name());
5224                if (layoutTurnout1.getContinuingSense() == Turnout.CLOSED) {
5225                    placeA1TToT(signalHeadName);
5226                } else {
5227                    placeB1TToT(signalHeadName);
5228                }
5229                removeAssignment(a1TToTHead);
5230                layoutTurnout1.setSignalB1Name(signalHeadName);
5231                needRedraw = true;
5232            }
5233        } else {
5234            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a1TToTHead, layoutTurnout1);
5235            if (assigned == LayoutTurnout.Geometry.NONE) {
5236                if (isHeadOnPanel(a1TToTHead)
5237                        && isHeadAssignedAnywhere(a1TToTHead)) {
5238                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5239                            Bundle.getMessage("SignalsError8",
5240                                    new Object[]{signalHeadName}),
5241                            Bundle.getMessage("ErrorTitle"),
5242                            JOptionPane.ERROR_MESSAGE);
5243                    return;
5244                } else {
5245                    removeSignalHeadFromPanel(layoutTurnout1.getSignalB1Name());
5246                    removeAssignment(a1TToTHead);
5247                    layoutTurnout1.setSignalB1Name(signalHeadName);
5248                }
5249                //} else if (assigned != B1) {
5250                //need to figure out what to do in this case - assigned to a different position on the same turnout.
5251            }
5252        }
5253
5254        signalHeadName = a2TToTSignalHeadComboBox.getSelectedItemDisplayName();
5255        if (signalHeadName == null) {
5256            signalHeadName = "";
5257        }
5258        if ((a2TToTHead != null) && setA2TToTHead.isSelected()) {
5259            if (isHeadOnPanel(a2TToTHead)
5260                    && (a2TToTHead != getHeadFromName(layoutTurnout1.getSignalB2Name()))) {
5261                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5262                        Bundle.getMessage("SignalsError6",
5263                                new Object[]{signalHeadName}),
5264                        Bundle.getMessage("ErrorTitle"),
5265                        JOptionPane.ERROR_MESSAGE);
5266                return;
5267            } else {
5268                removeSignalHeadFromPanel(layoutTurnout1.getSignalB2Name());
5269                if (layoutTurnout1.getContinuingSense() == Turnout.CLOSED) {
5270                    placeA2TToT(signalHeadName);
5271                } else {
5272                    placeB2TToT(signalHeadName);
5273                }
5274                removeAssignment(a2TToTHead);
5275                layoutTurnout1.setSignalB2Name(signalHeadName);
5276                needRedraw = true;
5277            }
5278        } else if (a2TToTHead != null) {
5279            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a2TToTHead, layoutTurnout1);
5280            if (assigned == LayoutTurnout.Geometry.NONE) {
5281                if (isHeadOnPanel(a2TToTHead)
5282                        && isHeadAssignedAnywhere(a2TToTHead)) {
5283                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5284                            Bundle.getMessage("SignalsError8",
5285                                    new Object[]{signalHeadName}),
5286                            Bundle.getMessage("ErrorTitle"),
5287                            JOptionPane.ERROR_MESSAGE);
5288                    return;
5289                } else {
5290                    removeSignalHeadFromPanel(layoutTurnout1.getSignalB2Name());
5291                    removeAssignment(a2TToTHead);
5292                    layoutTurnout1.setSignalB2Name(signalHeadName);
5293                }
5294                //} else if (assigned != B2) {
5295                //need to figure out what to do in this case.
5296            }
5297        } else { //a2TToTHead known to be null here
5298            removeSignalHeadFromPanel(layoutTurnout1.getSignalB2Name());
5299            layoutTurnout1.setSignalB2Name("");
5300        }
5301
5302        signalHeadName = b1TToTSignalHeadComboBox.getSelectedItemDisplayName();
5303        if (signalHeadName == null) {
5304            signalHeadName = "";
5305        }
5306        if (setB1TToTHead.isSelected()) {
5307            if (isHeadOnPanel(b1TToTHead)
5308                    && (b1TToTHead != getHeadFromName(layoutTurnout1.getSignalC1Name()))) {
5309                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5310                        Bundle.getMessage("SignalsError6",
5311                                new Object[]{signalHeadName}),
5312                        Bundle.getMessage("ErrorTitle"),
5313                        JOptionPane.ERROR_MESSAGE);
5314                return;
5315            } else {
5316                removeSignalHeadFromPanel(layoutTurnout1.getSignalC1Name());
5317                if (layoutTurnout1.getContinuingSense() == Turnout.CLOSED) {
5318                    placeB1TToT(signalHeadName);
5319                } else {
5320                    placeA1TToT(signalHeadName);
5321                }
5322                removeAssignment(b1TToTHead);
5323                layoutTurnout1.setSignalC1Name(signalHeadName);
5324                needRedraw = true;
5325            }
5326        } else {
5327            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b1TToTHead, layoutTurnout1);
5328            if (assigned == LayoutTurnout.Geometry.NONE) {
5329                if (isHeadOnPanel(b1TToTHead)
5330                        && isHeadAssignedAnywhere(b1TToTHead)) {
5331                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5332                            Bundle.getMessage("SignalsError8",
5333                                    new Object[]{signalHeadName}),
5334                            Bundle.getMessage("ErrorTitle"),
5335                            JOptionPane.ERROR_MESSAGE);
5336                    return;
5337                } else {
5338                    removeSignalHeadFromPanel(layoutTurnout1.getSignalC1Name());
5339                    removeAssignment(b1TToTHead);
5340                    layoutTurnout1.setSignalC1Name(signalHeadName);
5341                }
5342                //} else if (assigned != C1) {
5343                //need to figure out what to do in this case.
5344            }
5345        }
5346
5347        signalHeadName = b2TToTSignalHeadComboBox.getSelectedItemDisplayName();
5348        if (signalHeadName == null) {
5349            signalHeadName = "";
5350        }
5351        if ((b2TToTHead != null) && setB2TToTHead.isSelected()) {
5352            if (isHeadOnPanel(b2TToTHead)
5353                    && (b2TToTHead != getHeadFromName(layoutTurnout1.getSignalC2Name()))) {
5354                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5355                        Bundle.getMessage("SignalsError6",
5356                                new Object[]{signalHeadName}),
5357                        Bundle.getMessage("ErrorTitle"),
5358                        JOptionPane.ERROR_MESSAGE);
5359                return;
5360            } else {
5361                removeSignalHeadFromPanel(layoutTurnout1.getSignalC2Name());
5362                if (layoutTurnout1.getContinuingSense() == Turnout.CLOSED) {
5363                    placeB2TToT(signalHeadName);
5364                } else {
5365                    placeA2TToT(signalHeadName);
5366                }
5367                removeAssignment(b2TToTHead);
5368                layoutTurnout1.setSignalC2Name(signalHeadName);
5369                needRedraw = true;
5370            }
5371        } else if (b2TToTHead != null) {
5372            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b2TToTHead, layoutTurnout1);
5373            if (assigned == LayoutTurnout.Geometry.NONE) {
5374                if (isHeadOnPanel(b2TToTHead)
5375                        && isHeadAssignedAnywhere(b2TToTHead)) {
5376                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5377                            Bundle.getMessage("SignalsError8",
5378                                    new Object[]{signalHeadName}),
5379                            Bundle.getMessage("ErrorTitle"),
5380                            JOptionPane.ERROR_MESSAGE);
5381                    return;
5382                } else {
5383                    removeSignalHeadFromPanel(layoutTurnout1.getSignalC2Name());
5384                    removeAssignment(b2TToTHead);
5385                    layoutTurnout1.setSignalC2Name(signalHeadName);
5386                }
5387                //} else if (assigned != C2) {
5388                //need to figure out what to do in this case.
5389            }
5390        } else { //b2TToTHead known to be null here
5391            removeSignalHeadFromPanel(layoutTurnout1.getSignalC2Name());
5392            layoutTurnout1.setSignalC2Name("");
5393        }
5394
5395        //signal heads on turnout 2
5396        signalHeadName = c1TToTSignalHeadComboBox.getSelectedItemDisplayName();
5397        if (signalHeadName == null) {
5398            signalHeadName = "";
5399        }
5400        if (setC1TToTHead.isSelected()) {
5401            if (isHeadOnPanel(c1TToTHead)
5402                    && (c1TToTHead != getHeadFromName(layoutTurnout2.getSignalB1Name()))) {
5403                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5404                        Bundle.getMessage("SignalsError6",
5405                                new Object[]{signalHeadName}),
5406                        Bundle.getMessage("ErrorTitle"),
5407                        JOptionPane.ERROR_MESSAGE);
5408                return;
5409            } else {
5410                removeSignalHeadFromPanel(layoutTurnout2.getSignalB1Name());
5411                if (layoutTurnout2.getContinuingSense() == Turnout.CLOSED) {
5412                    placeC1TToT(signalHeadName);
5413                } else {
5414                    placeD1TToT(signalHeadName);
5415                }
5416                removeAssignment(c1TToTHead);
5417                layoutTurnout2.setSignalB1Name(signalHeadName);
5418                needRedraw = true;
5419            }
5420        } else {
5421            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c1TToTHead, layoutTurnout2);
5422            if (assigned == LayoutTurnout.Geometry.NONE) {
5423                if (isHeadOnPanel(c1TToTHead)
5424                        && isHeadAssignedAnywhere(c1TToTHead)) {
5425                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5426                            Bundle.getMessage("SignalsError8",
5427                                    new Object[]{signalHeadName}),
5428                            Bundle.getMessage("ErrorTitle"),
5429                            JOptionPane.ERROR_MESSAGE);
5430                    return;
5431                } else {
5432                    removeSignalHeadFromPanel(layoutTurnout2.getSignalB1Name());
5433                    removeAssignment(c1TToTHead);
5434                    layoutTurnout2.setSignalB1Name(signalHeadName);
5435                }
5436                //} else if (assigned != B1) {
5437                //need to figure out what to do in this case.
5438            }
5439        }
5440
5441        signalHeadName = c2TToTSignalHeadComboBox.getSelectedItemDisplayName();
5442        if (signalHeadName == null) {
5443            signalHeadName = "";
5444        }
5445        if ((c2TToTHead != null) && setC2TToTHead.isSelected()) {
5446            if (isHeadOnPanel(c2TToTHead)
5447                    && (c2TToTHead != getHeadFromName(layoutTurnout2.getSignalB2Name()))) {
5448                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5449                        Bundle.getMessage("SignalsError6",
5450                                new Object[]{signalHeadName}),
5451                        Bundle.getMessage("ErrorTitle"),
5452                        JOptionPane.ERROR_MESSAGE);
5453                return;
5454            } else {
5455                removeSignalHeadFromPanel(layoutTurnout2.getSignalB2Name());
5456                if (layoutTurnout2.getContinuingSense() == Turnout.CLOSED) {
5457                    placeC2TToT(signalHeadName);
5458                } else {
5459                    placeD2TToT(signalHeadName);
5460                }
5461                removeAssignment(c2TToTHead);
5462                layoutTurnout2.setSignalB2Name(signalHeadName);
5463                needRedraw = true;
5464            }
5465        } else if (c2TToTHead != null) {
5466            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c2TToTHead, layoutTurnout2);
5467            if (assigned == LayoutTurnout.Geometry.NONE) {
5468                if (isHeadOnPanel(c2TToTHead)
5469                        && isHeadAssignedAnywhere(c2TToTHead)) {
5470                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5471                            Bundle.getMessage("SignalsError8",
5472                                    new Object[]{signalHeadName}),
5473                            Bundle.getMessage("ErrorTitle"),
5474                            JOptionPane.ERROR_MESSAGE);
5475                    return;
5476                } else {
5477                    removeSignalHeadFromPanel(layoutTurnout2.getSignalB2Name());
5478                    removeAssignment(c2TToTHead);
5479                    layoutTurnout2.setSignalB2Name(signalHeadName);
5480                }
5481                //} else if (assigned != B2) {
5482                //need to figure out what to do in this case.
5483            }
5484        } else { //c2TToTHead known to be null here
5485            removeSignalHeadFromPanel(layoutTurnout2.getSignalB2Name());
5486            layoutTurnout2.setSignalB2Name("");
5487        }
5488
5489        signalHeadName = d1TToTSignalHeadComboBox.getSelectedItemDisplayName();
5490        if (signalHeadName == null) {
5491            signalHeadName = "";
5492        }
5493        if (setD1TToTHead.isSelected()) {
5494            if (isHeadOnPanel(d1TToTHead)
5495                    && (d1TToTHead != getHeadFromName(layoutTurnout2.getSignalC1Name()))) {
5496                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5497                        Bundle.getMessage("SignalsError6",
5498                                new Object[]{signalHeadName}),
5499                        Bundle.getMessage("ErrorTitle"),
5500                        JOptionPane.ERROR_MESSAGE);
5501                return;
5502            } else {
5503                removeSignalHeadFromPanel(layoutTurnout2.getSignalC1Name());
5504                if (layoutTurnout2.getContinuingSense() == Turnout.CLOSED) {
5505                    placeD1TToT(signalHeadName);
5506                } else {
5507                    placeC1TToT(signalHeadName);
5508                }
5509                removeAssignment(d1TToTHead);
5510                layoutTurnout2.setSignalC1Name(signalHeadName);
5511                needRedraw = true;
5512            }
5513        } else {
5514            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d1TToTHead, layoutTurnout2);
5515            if (assigned == LayoutTurnout.Geometry.NONE) {
5516                if (isHeadOnPanel(d1TToTHead)
5517                        && isHeadAssignedAnywhere(d1TToTHead)) {
5518                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5519                            Bundle.getMessage("SignalsError8",
5520                                    new Object[]{signalHeadName}),
5521                            Bundle.getMessage("ErrorTitle"),
5522                            JOptionPane.ERROR_MESSAGE);
5523                    return;
5524                } else {
5525                    removeSignalHeadFromPanel(layoutTurnout2.getSignalC1Name());
5526                    removeAssignment(d1TToTHead);
5527                    layoutTurnout2.setSignalC1Name(signalHeadName);
5528                }
5529                //} else if (assigned != C1) {
5530                //need to figure out what to do in this case.
5531            }
5532        }
5533
5534        signalHeadName = d2TToTSignalHeadComboBox.getSelectedItemDisplayName();
5535        if (signalHeadName == null) {
5536            signalHeadName = "";
5537        }
5538        if ((d2TToTHead != null) && setD2TToTHead.isSelected()) {
5539            if (isHeadOnPanel(d2TToTHead)
5540                    && (d2TToTHead != getHeadFromName(layoutTurnout2.getSignalC2Name()))) {
5541                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5542                        Bundle.getMessage("SignalsError6",
5543                                new Object[]{signalHeadName}),
5544                        Bundle.getMessage("ErrorTitle"),
5545                        JOptionPane.ERROR_MESSAGE);
5546                return;
5547            } else {
5548                removeSignalHeadFromPanel(layoutTurnout2.getSignalC2Name());
5549                if (layoutTurnout2.getContinuingSense() == Turnout.CLOSED) {
5550                    placeD2TToT(signalHeadName);
5551                } else {
5552                    placeC2TToT(signalHeadName);
5553                }
5554                removeAssignment(d2TToTHead);
5555                layoutTurnout2.setSignalC2Name(signalHeadName);
5556                needRedraw = true;
5557            }
5558        } else if (d2TToTHead != null) {
5559            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d2TToTHead, layoutTurnout2);
5560            if (assigned == LayoutTurnout.Geometry.NONE) {
5561                if (isHeadOnPanel(d2TToTHead)
5562                        && isHeadAssignedAnywhere(d2TToTHead)) {
5563                    JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5564                            Bundle.getMessage("SignalsError8",
5565                                    new Object[]{signalHeadName}),
5566                            Bundle.getMessage("ErrorTitle"),
5567                            JOptionPane.ERROR_MESSAGE);
5568                    return;
5569                } else {
5570                    removeSignalHeadFromPanel(layoutTurnout2.getSignalC2Name());
5571                    removeAssignment(d2TToTHead);
5572                    layoutTurnout2.setSignalC2Name(signalHeadName);
5573                }
5574                //} else if (assigned != C2) {
5575                //need to figure out what to do in this case.
5576            }
5577        } else { //d2TToTHead known to be null here
5578            removeSignalHeadFromPanel(layoutTurnout2.getSignalC2Name());
5579            layoutTurnout2.setSignalC2Name("");
5580        }
5581
5582        //setup logic if requested
5583        if (setupA1TToTLogic.isSelected() || setupA2TToTLogic.isSelected()) {
5584            setLogicTToT(a1TToTHead, (TrackSegment) layoutTurnout2.getConnectB(), a2TToTHead,
5585                    (TrackSegment) layoutTurnout2.getConnectC(), setupA1TToTLogic.isSelected(),
5586                    setupA2TToTLogic.isSelected(), true, layoutTurnout2, layoutTurnout1);
5587        }
5588        if (setupB1TToTLogic.isSelected() || setupB2TToTLogic.isSelected()) {
5589            setLogicTToT(b1TToTHead, (TrackSegment) layoutTurnout2.getConnectB(), b2TToTHead,
5590                    (TrackSegment) layoutTurnout2.getConnectC(), setupB1TToTLogic.isSelected(),
5591                    setupB2TToTLogic.isSelected(), false, layoutTurnout2, layoutTurnout1);
5592        }
5593        if (setupC1TToTLogic.isSelected() || setupC2TToTLogic.isSelected()) {
5594            setLogicTToT(c1TToTHead, (TrackSegment) layoutTurnout1.getConnectB(), c2TToTHead,
5595                    (TrackSegment) layoutTurnout1.getConnectC(), setupC1TToTLogic.isSelected(),
5596                    setupC2TToTLogic.isSelected(), true, layoutTurnout1, layoutTurnout2);
5597        }
5598        if (setupD1TToTLogic.isSelected() || setupD2TToTLogic.isSelected()) {
5599            setLogicTToT(d1TToTHead, (TrackSegment) layoutTurnout1.getConnectB(), d2TToTHead,
5600                    (TrackSegment) layoutTurnout1.getConnectC(), setupD1TToTLogic.isSelected(),
5601                    setupD2TToTLogic.isSelected(), false, layoutTurnout1, layoutTurnout2);
5602        }
5603        //link the two turnouts
5604        layoutTurnout1.setLinkedTurnoutName(turnout2ComboBox.getSelectedItemDisplayName());
5605        layoutTurnout1.setLinkType(LayoutTurnout.LinkType.THROAT_TO_THROAT);
5606        layoutTurnout2.setLinkedTurnoutName(turnout1ComboBox.getSelectedItemDisplayName());
5607        layoutTurnout2.setLinkType(LayoutTurnout.LinkType.THROAT_TO_THROAT);
5608        //finish up
5609        setSignalsAtThroatToThroatTurnoutsOpenFlag = false;
5610        setSignalsAtThroatToThroatTurnoutsFrame.setVisible(false);
5611        if (needRedraw) {
5612            layoutEditor.redrawPanel();
5613            needRedraw = false;
5614            layoutEditor.setDirty();
5615        }
5616    }   //setTToTSignalsDonePressed
5617
5618    private boolean getTToTSignalHeadInformation() {
5619        a1TToTHead = getSignalHeadFromEntry(a1TToTSignalHeadComboBox, true, setSignalsAtThroatToThroatTurnoutsFrame);
5620        if (a1TToTHead == null) {
5621            return false;
5622        }
5623        a2TToTHead = getSignalHeadFromEntry(a2TToTSignalHeadComboBox, false, setSignalsAtThroatToThroatTurnoutsFrame);
5624        b1TToTHead = getSignalHeadFromEntry(b1TToTSignalHeadComboBox, true, setSignalsAtThroatToThroatTurnoutsFrame);
5625        if (b1TToTHead == null) {
5626            return false;
5627        }
5628        b2TToTHead = getSignalHeadFromEntry(b2TToTSignalHeadComboBox, false, setSignalsAtThroatToThroatTurnoutsFrame);
5629        c1TToTHead = getSignalHeadFromEntry(c1TToTSignalHeadComboBox, true, setSignalsAtThroatToThroatTurnoutsFrame);
5630        if (c1TToTHead == null) {
5631            return false;
5632        }
5633        c2TToTHead = getSignalHeadFromEntry(c2TToTSignalHeadComboBox, false, setSignalsAtThroatToThroatTurnoutsFrame);
5634        d1TToTHead = getSignalHeadFromEntry(d1TToTSignalHeadComboBox, true, setSignalsAtThroatToThroatTurnoutsFrame);
5635        if (d1TToTHead == null) {
5636            return false;
5637        }
5638        d2TToTHead = getSignalHeadFromEntry(d2TToTSignalHeadComboBox, false, setSignalsAtThroatToThroatTurnoutsFrame);
5639        return true;
5640    }
5641
5642    private void placeA1TToT(String signalHeadName) {
5643        //place head near the continuing track of turnout 1
5644        if (testIcon == null) {
5645            testIcon = signalIconEditor.getIcon(0);
5646        }
5647        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5648
5649        LayoutTurnoutView layoutTurnout1View = layoutEditor.getLayoutTurnoutView(layoutTurnout1);
5650        Point2D coordsB = layoutTurnout1View.getCoordsB();
5651        Point2D coordsCenter = layoutTurnout1View.getCoordsCenter();
5652
5653        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5654        Point2D delta = new Point2D.Double(0.0, -shift);
5655
5656        delta = MathUtil.rotateDEG(delta, bDirDEG);
5657        Point2D where = MathUtil.add(coordsB, delta);
5658        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
5659    }
5660
5661    private void placeA2TToT(String signalHeadName) {
5662        if (testIcon == null) {
5663            testIcon = signalIconEditor.getIcon(0);
5664        }
5665        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5666
5667        LayoutTurnoutView layoutTurnout1View = layoutEditor.getLayoutTurnoutView(layoutTurnout1);
5668        Point2D coordsB = layoutTurnout1View.getCoordsB();
5669        Point2D coordsCenter = layoutTurnout1View.getCoordsCenter();
5670
5671        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5672        Point2D delta = new Point2D.Double(2.0 * shift, -shift);
5673
5674        delta = MathUtil.rotateDEG(delta, bDirDEG);
5675        Point2D where = MathUtil.add(coordsB, delta);
5676        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
5677    }
5678
5679    private void placeB1TToT(String signalHeadName) {
5680        if (testIcon == null) {
5681            testIcon = signalIconEditor.getIcon(0);
5682        }
5683        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5684
5685        LayoutTurnoutView layoutTurnout1View = layoutEditor.getLayoutTurnoutView(layoutTurnout1);
5686        Point2D coordsB = layoutTurnout1View.getCoordsB();
5687        Point2D coordsC = layoutTurnout1View.getCoordsC();
5688        Point2D coordsCenter = layoutTurnout1View.getCoordsCenter();
5689
5690        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5691        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
5692        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
5693        double shiftX = 0.0;
5694        if (diffDirDEG >= 0.0) {
5695            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
5696        }
5697        Point2D delta = new Point2D.Double(shiftX, -shift);
5698
5699        delta = MathUtil.rotateDEG(delta, cDirDEG);
5700        Point2D where = MathUtil.add(coordsC, delta);
5701        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
5702    }
5703
5704    private void placeB2TToT(String signalHeadName) {
5705        if (testIcon == null) {
5706            testIcon = signalIconEditor.getIcon(0);
5707        }
5708        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5709
5710        LayoutTurnoutView layoutTurnout1View = layoutEditor.getLayoutTurnoutView(layoutTurnout1);
5711        Point2D coordsB = layoutTurnout1View.getCoordsB();
5712        Point2D coordsC = layoutTurnout1View.getCoordsC();
5713        Point2D coordsCenter = layoutTurnout1View.getCoordsCenter();
5714
5715        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5716        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
5717        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
5718        double shiftX = 2.0 * shift;
5719        if (diffDirDEG >= 0.0) {
5720            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
5721        }
5722        Point2D delta = new Point2D.Double(shiftX, -shift);
5723
5724        delta = MathUtil.rotateDEG(delta, cDirDEG);
5725        Point2D where = MathUtil.add(coordsC, delta);
5726        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
5727    }
5728
5729    private void placeC1TToT(String signalHeadName) {
5730        if (testIcon == null) {
5731            testIcon = signalIconEditor.getIcon(0);
5732        }
5733        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5734
5735        LayoutTurnoutView layoutTurnout2View = layoutEditor.getLayoutTurnoutView(layoutTurnout2);
5736        Point2D coordsB = layoutTurnout2View.getCoordsB();
5737        Point2D coordsCenter = layoutTurnout2View.getCoordsCenter();
5738
5739        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5740        Point2D delta = new Point2D.Double(0.0, -shift);
5741
5742        delta = MathUtil.rotateDEG(delta, bDirDEG);
5743        Point2D where = MathUtil.add(coordsB, delta);
5744        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
5745    }
5746
5747    private void placeC2TToT(String signalHeadName) {
5748        if (testIcon == null) {
5749            testIcon = signalIconEditor.getIcon(0);
5750        }
5751        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5752
5753        LayoutTurnoutView layoutTurnout2View = layoutEditor.getLayoutTurnoutView(layoutTurnout2);
5754        Point2D coordsB = layoutTurnout2View.getCoordsB();
5755        Point2D coordsCenter = layoutTurnout2View.getCoordsCenter();
5756
5757        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5758        Point2D delta = new Point2D.Double(2.0 * shift, -shift);
5759
5760        delta = MathUtil.rotateDEG(delta, bDirDEG);
5761        Point2D where = MathUtil.add(coordsB, delta);
5762        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
5763    }
5764
5765    private void placeD1TToT(String signalHeadName) {
5766        if (testIcon == null) {
5767            testIcon = signalIconEditor.getIcon(0);
5768        }
5769        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5770
5771        LayoutTurnoutView layoutTurnout2View = layoutEditor.getLayoutTurnoutView(layoutTurnout2);
5772        Point2D coordsB = layoutTurnout2View.getCoordsB();
5773        Point2D coordsC = layoutTurnout2View.getCoordsC();
5774        Point2D coordsCenter = layoutTurnout2View.getCoordsCenter();
5775
5776        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5777        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
5778        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
5779        double shiftX = 0.0;
5780        if (diffDirDEG >= 0.0) {
5781            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
5782        }
5783        Point2D delta = new Point2D.Double(shiftX, -shift);
5784
5785        delta = MathUtil.rotateDEG(delta, cDirDEG);
5786        Point2D where = MathUtil.add(coordsC, delta);
5787        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
5788    }
5789
5790    private void placeD2TToT(String signalHeadName) {
5791        if (testIcon == null) {
5792            testIcon = signalIconEditor.getIcon(0);
5793        }
5794        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5795
5796        LayoutTurnoutView layoutTurnout2View = layoutEditor.getLayoutTurnoutView(layoutTurnout2);
5797        Point2D coordsB = layoutTurnout2View.getCoordsB();
5798        Point2D coordsC = layoutTurnout2View.getCoordsC();
5799        Point2D coordsCenter = layoutTurnout2View.getCoordsCenter();
5800
5801        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5802        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
5803        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
5804        double shiftX = 2.0 * shift;
5805        if (diffDirDEG >= 0.0) {
5806            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
5807        }
5808        Point2D delta = new Point2D.Double(shiftX, -shift);
5809
5810        delta = MathUtil.rotateDEG(delta, cDirDEG);
5811        Point2D where = MathUtil.add(coordsC, delta);
5812        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
5813    }
5814
5815    @SuppressWarnings("null")
5816    private void setLogicTToT(SignalHead head, TrackSegment track1, SignalHead secondHead, TrackSegment track2,
5817            boolean setup1, boolean setup2, boolean continuing,
5818            LayoutTurnout farTurnout, LayoutTurnout nearTurnout) {
5819        //initialize common components and ensure all is defined
5820        LayoutBlock connectorBlock = connectorTrack.getLayoutBlock();
5821        LayoutBlock nearTurnoutBlock = nearTurnout.getLayoutBlock();
5822        LayoutBlock farTurnoutBlock = farTurnout.getLayoutBlock();
5823        Sensor connectorOccupancy = null;
5824        if ((connectorBlock == null) || (nearTurnoutBlock == null) || (farTurnoutBlock == null)) {
5825            JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5826                    Bundle.getMessage("InfoMessage6"),
5827                    Bundle.getMessage("MessageTitle"),
5828                    JOptionPane.INFORMATION_MESSAGE);
5829            return;
5830        }
5831        connectorOccupancy = connectorBlock.getOccupancySensor();
5832        if (connectorOccupancy == null) {
5833            JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5834                    Bundle.getMessage("InfoMessage4",
5835                            new Object[]{connectorBlock.getUserName()}),
5836                    Bundle.getMessage("MessageTitle"),
5837                    JOptionPane.INFORMATION_MESSAGE);
5838            return;
5839        }
5840        //setup signal head for continuing track of far turnout (or both tracks of far turnout)
5841        if ((track1 == null) && setup1) {
5842            JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5843                    Bundle.getMessage("InfoMessage7"),
5844                    Bundle.getMessage("MessageTitle"),
5845                    JOptionPane.INFORMATION_MESSAGE);
5846            return;
5847        }
5848        Sensor occupancy = null;
5849        SignalHead nextHead = null;
5850        if ((track1 != null) && setup1) {
5851            LayoutBlock block = track1.getLayoutBlock();
5852            if (block == null) {
5853                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5854                        Bundle.getMessage("InfoMessage6"),
5855                        Bundle.getMessage("MessageTitle"),
5856                        JOptionPane.INFORMATION_MESSAGE);
5857                return;
5858            }
5859            occupancy = block.getOccupancySensor();
5860            if (occupancy == null) {
5861                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5862                        Bundle.getMessage("InfoMessage4",
5863                                new Object[]{block.getUserName()}),
5864                        Bundle.getMessage("MessageTitle"),
5865                        JOptionPane.INFORMATION_MESSAGE);
5866                return;
5867            }
5868            nextHead = getNextSignalFromObject(track1, farTurnout,
5869                    head.getSystemName(), setSignalsAtThroatToThroatTurnoutsFrame);
5870            if ((nextHead == null) && (!reachedEndBumper())) {
5871                JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
5872                        Bundle.getMessage("InfoMessage5",
5873                                new Object[]{block.getUserName()}),
5874                        Bundle.getMessage("MessageTitle"),
5875                        JOptionPane.INFORMATION_MESSAGE);
5876                return;
5877            }
5878            if (secondHead != null) {
5879                //this head signals only the continuing track of the far turnout
5880                if (!initializeBlockBossLogic(head.getDisplayName())) {
5881                    return;
5882                }
5883                logic.setMode(BlockBossLogic.TRAILINGMAIN);
5884                logic.setTurnout(farTurnout.getTurnout().getDisplayName());
5885                logic.setSensor1(occupancy.getDisplayName());
5886                if (occupancy != connectorOccupancy) {
5887                    logic.setSensor2(connectorOccupancy.getDisplayName());
5888                }
5889                if (nextHead != null) {
5890                    logic.setWatchedSignal1(nextHead.getDisplayName(), false);
5891                }
5892                if (auxSignal != null) {
5893                    logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
5894                }
5895                String nearSensorName = setupNearLogix(nearTurnout, continuing, head);
5896                addNearSensorToLogic(nearSensorName);
5897                finalizeBlockBossLogic();
5898            }
5899        }
5900        if ((secondHead != null) && !setup2) {
5901            return;
5902        }
5903        SignalHead savedAuxSignal = auxSignal;
5904        if (track2 == null) {
5905            JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5906                    Bundle.getMessage("InfoMessage7"),
5907                    Bundle.getMessage("MessageTitle"),
5908                    JOptionPane.INFORMATION_MESSAGE);
5909            return;
5910        }
5911        LayoutBlock block2 = track2.getLayoutBlock();
5912        if (block2 == null) {
5913            JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5914                    Bundle.getMessage("InfoMessage6"),
5915                    Bundle.getMessage("MessageTitle"),
5916                    JOptionPane.INFORMATION_MESSAGE);
5917            return;
5918        }
5919        Sensor occupancy2 = block2.getOccupancySensor();
5920        if (occupancy2 == null) {
5921            JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5922                    Bundle.getMessage("InfoMessage4",
5923                            new Object[]{block2.getUserName()}),
5924                    Bundle.getMessage("MessageTitle"),
5925                    JOptionPane.INFORMATION_MESSAGE);
5926            return;
5927        }
5928        SignalHead nextHead2 = null;
5929        if (secondHead != null) {
5930            nextHead2 = getNextSignalFromObject(track2,
5931                    farTurnout, secondHead.getSystemName(), setSignalsAtThroatToThroatTurnoutsFrame);
5932            if ((nextHead2 == null) && (!reachedEndBumper())) {
5933                JOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5934                        Bundle.getMessage("InfoMessage5",
5935                                new Object[]{block2.getUserName()}),
5936                        Bundle.getMessage("MessageTitle"),
5937                        JOptionPane.INFORMATION_MESSAGE);
5938                return;
5939            }
5940        }
5941        if ((secondHead == null) && (track1 != null) && setup1) {
5942            if (!initializeBlockBossLogic(head.getDisplayName())) {
5943                return;
5944            }
5945            logic.setMode(BlockBossLogic.FACING);
5946            logic.setTurnout(farTurnout.getTurnout().getDisplayName());
5947            logic.setWatchedSensor1(occupancy.getDisplayName());
5948            logic.setWatchedSensor2(occupancy2.getDisplayName());
5949            logic.setSensor2(connectorOccupancy.getDisplayName());
5950            if (nextHead != null) {
5951                logic.setWatchedSignal1(nextHead.getDisplayName(), false);
5952            }
5953            if (savedAuxSignal != null) {
5954                logic.setWatchedSignal1Alt(savedAuxSignal.getDisplayName());
5955            }
5956            if (nextHead2 != null) {
5957                logic.setWatchedSignal2(nextHead2.getDisplayName());
5958            }
5959            if (auxSignal != null) {
5960                logic.setWatchedSignal2Alt(auxSignal.getDisplayName());
5961            }
5962            String nearSensorName = setupNearLogix(nearTurnout, continuing, head);
5963            addNearSensorToLogic(nearSensorName);
5964            logic.setLimitSpeed2(true);
5965            finalizeBlockBossLogic();
5966        } else if ((secondHead != null) && setup2) {
5967            if (!initializeBlockBossLogic(secondHead.getDisplayName())) {
5968                return;
5969            }
5970            logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
5971            logic.setTurnout(farTurnout.getTurnout().getDisplayName());
5972            logic.setSensor1(occupancy2.getDisplayName());
5973            if (occupancy2 != connectorOccupancy) {
5974                logic.setSensor2(connectorOccupancy.getDisplayName());
5975            }
5976            if (nextHead2 != null) {
5977                logic.setWatchedSignal1(nextHead2.getDisplayName(), false);
5978            }
5979            if (auxSignal != null) {
5980                logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
5981            }
5982            String nearSensorName = setupNearLogix(nearTurnout, continuing, head);
5983            addNearSensorToLogic(nearSensorName);
5984            logic.setLimitSpeed2(true);
5985            finalizeBlockBossLogic();
5986        }
5987    }   //setLogicTToT
5988
5989    /*
5990     * Sets up a Logix to set a sensor active if a turnout is set against
5991     *  a track.  This routine creates an internal sensor for the purpose.
5992     * Note: The sensor and logix are named pref + S or pref + X followed by TTT_X_HHH where
5993     *  TTT is the system name of the turnout, X is either C or T depending
5994     *  on "continuing", and HHH is the system name of the signal head.
5995     * Note: If there is any problem, a string of "" is returned, and a warning
5996     *  message is issued.
5997     */
5998    private String setupNearLogix(LayoutTurnout nearTurnout, boolean continuing,
5999            SignalHead head) {
6000        String turnoutName = nearTurnout.getTurnout().getSystemName();
6001        String namer = turnoutName + "_T_" + head.getSystemName();
6002        if (!continuing) {
6003            namer = turnoutName + "_C_" + head.getSystemName();
6004        }
6005        String pref = InstanceManager.getDefault(jmri.LogixManager.class).getSystemPrefix();
6006        String sensorName = pref + "S" + namer;
6007        String logixName = pref + "X" + namer;
6008        try {
6009            InstanceManager.sensorManagerInstance().provideSensor(sensorName);
6010        } catch (IllegalArgumentException ex) {
6011            log.error("Trouble creating sensor {} while setting up Logix.", sensorName);
6012            return "";
6013
6014        }
6015        if (InstanceManager.getDefault(LogixManager.class).getBySystemName(logixName) == null) {
6016            //Logix does not exist, create it
6017            Logix x = InstanceManager.getDefault(LogixManager.class).createNewLogix(logixName, "");
6018            if (x == null) {
6019                log.error("Trouble creating logix {} while setting up signal logic.", logixName);
6020                return "";
6021            }
6022            String cName = x.getSystemName() + "C1";
6023            Conditional c = InstanceManager.getDefault(ConditionalManager.class).
6024                    createNewConditional(cName, "");
6025            if (c == null) {
6026                log.error("Trouble creating conditional {} while setting up Logix.", cName);
6027                return "";
6028            }
6029            Conditional.Type type = Conditional.Type.TURNOUT_THROWN;
6030            if (!continuing) {
6031                type = Conditional.Type.TURNOUT_CLOSED;
6032            }
6033            List<ConditionalVariable> variableList = c.getCopyOfStateVariables();
6034            variableList.add(new ConditionalVariable(false, Conditional.Operator.AND,
6035                    type, turnoutName, true));
6036            c.setStateVariables(variableList);
6037            List<ConditionalAction> actionList = c.getCopyOfActions();
6038            actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE,
6039                    Conditional.Action.SET_SENSOR, sensorName,
6040                    Sensor.ACTIVE, ""));
6041            actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_FALSE,
6042                    Conditional.Action.SET_SENSOR, sensorName,
6043                    Sensor.INACTIVE, ""));
6044            c.setAction(actionList); // string data
6045            x.addConditional(cName, -1);
6046            x.activateLogix();
6047        }
6048        return sensorName;
6049    }
6050
6051    /*
6052     * Adds the sensor specified to the open BlockBossLogic, provided it is not already there and
6053     *  provided there is an open slot. If 'name' is null or empty, returns without doing anything.
6054     */
6055    private void addNearSensorToLogic(String name) {
6056        if ((name != null) && !name.isEmpty()) {
6057            //return if a sensor by this name is already present
6058            if ((logic.getSensor1() != null) && (logic.getSensor1().equals(name))) {
6059                return;
6060            }
6061            if ((logic.getSensor2() != null) && (logic.getSensor2().equals(name))) {
6062                return;
6063            }
6064            if ((logic.getSensor3() != null) && (logic.getSensor3().equals(name))) {
6065                return;
6066            }
6067            if ((logic.getSensor4() != null) && (logic.getSensor4().equals(name))) {
6068                return;
6069            }
6070            if ((logic.getSensor5() != null) && (logic.getSensor5().equals(name))) {
6071                return;
6072            }
6073            //add in the first available slot
6074            if (logic.getSensor1() == null) {
6075                logic.setSensor1(name);
6076            } else if (logic.getSensor2() == null) {
6077                logic.setSensor2(name);
6078            } else if (logic.getSensor3() == null) {
6079                logic.setSensor3(name);
6080            } else if (logic.getSensor4() == null) {
6081                logic.setSensor4(name);
6082            } else if (logic.getSensor5() == null) {
6083                logic.setSensor5(name);
6084            } else {
6085                log.error("Error - could not add sensor to SSL for signal head {}", logic.getDrivenSignal());
6086            }
6087        }
6088    }
6089
6090    /*=========================*\
6091    |* setSignalsAt3WayTurnout *|
6092    \*=========================*/
6093    /**
6094     * Tool to set signals at a three-way turnout, including placing the signal
6095     * icons and setup of signal logic for each signal head
6096     * <p>
6097     * This tool can only be accessed from the Tools menu. There is no access
6098     * from a turnout pop-up menu.
6099     * <p>
6100     * This tool requires a situation where two turnouts are connected to model
6101     * a 3-way turnout, with the throat of the second turnout connected to the
6102     * continuing leg of the first turnout by a very short track segment. The
6103     * actual length of the track segment is not tested. If this situation is
6104     * not found, and error message is sent to the user.
6105     * <p>
6106     * This tool assumes two turnouts connected with the throat of the second
6107     * turnout connected to the continuing leg of the first turnou, as used to
6108     * represent a 3-way turnout. The turnouts may be either left-handed, or
6109     * right-handed, or any pair of these. This tool also assumes that there are
6110     * no signals between the two turnouts. Signal heads are allowed/required at
6111     * the continuing leg of the second turnout, at each of the diverging legs,
6112     * and at the throat. At the throat, either one or three heads are provided
6113     * for. So four or six heads will be placed.
6114     * <p>
6115     * This tool assumes that each of the four tracks, the continuing, the two
6116     * diverging, and the throat is contained in a different block. The two
6117     * turnouts used to model the 3-way turnout must be in the same block.
6118     * Things work best if the two turnouts are in the same block as the track
6119     * connecting at the throat, or if the two turnouts are in their own
6120     * separate block, either works fine.
6121     */
6122    //operational variables for Set Signals at 3-Way Turnout tool
6123    private JmriJFrame setSignalsAt3WayTurnoutFrame = null;
6124    private boolean setSignalsAt3WayTurnoutOpenFlag = false;
6125    private boolean setSignalsAt3WayTurnoutFromMenuFlag = false;
6126
6127    private JLabel turnoutANameLabel = null;
6128    private JLabel turnoutBNameLabel = null;
6129
6130    private final NamedBeanComboBox<Turnout> turnoutAComboBox = new NamedBeanComboBox<>(
6131            InstanceManager.turnoutManagerInstance(),
6132            null, DisplayOptions.DISPLAYNAME);
6133    private final NamedBeanComboBox<Turnout> turnoutBComboBox = new NamedBeanComboBox<>(
6134            InstanceManager.turnoutManagerInstance(),
6135            null, DisplayOptions.DISPLAYNAME);
6136
6137    private final NamedBeanComboBox<SignalHead> a1_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6138            InstanceManager.getDefault(SignalHeadManager.class
6139            ),
6140            null, DisplayOptions.DISPLAYNAME);
6141    private final NamedBeanComboBox<SignalHead> a2_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6142            InstanceManager.getDefault(SignalHeadManager.class
6143            ),
6144            null, DisplayOptions.DISPLAYNAME);
6145    private final NamedBeanComboBox<SignalHead> a3_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6146            InstanceManager.getDefault(SignalHeadManager.class
6147            ),
6148            null, DisplayOptions.DISPLAYNAME);
6149    private final NamedBeanComboBox<SignalHead> b_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6150            InstanceManager.getDefault(SignalHeadManager.class
6151            ),
6152            null, DisplayOptions.DISPLAYNAME);
6153    private final NamedBeanComboBox<SignalHead> c_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6154            InstanceManager.getDefault(SignalHeadManager.class
6155            ),
6156            null, DisplayOptions.DISPLAYNAME);
6157    private final NamedBeanComboBox<SignalHead> d_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6158            InstanceManager.getDefault(SignalHeadManager.class
6159            ),
6160            null, DisplayOptions.DISPLAYNAME);
6161
6162    private final JCheckBox setA13WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6163    private final JCheckBox setupA13WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6164    private final JCheckBox setA23WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6165    private final JCheckBox setupA23WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6166    private final JCheckBox setA33WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6167    private final JCheckBox setupA33WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6168    private final JCheckBox setB3WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6169    private final JCheckBox setupB3WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6170    private final JCheckBox setC3WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6171    private final JCheckBox setupC3WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6172    private final JCheckBox setD3WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6173    private final JCheckBox setupD3WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6174    private JButton getSaved3WaySignalHeads = null;
6175    private JButton change3WaySignalIcon = null;
6176    private JButton set3WaySignalsDone = null;
6177    private JButton set3WaySignalsCancel = null;
6178    private LayoutTurnout layoutTurnoutA = null;
6179    private LayoutTurnout layoutTurnoutB = null;
6180    private Turnout turnoutA = null;
6181    private Turnout turnoutB = null;
6182    //private TrackSegment conTrack = null;
6183    private SignalHead a13WayHead = null; //saved in A1 of Turnout A - Throat - continuing
6184    private SignalHead a23WayHead = null; //saved in A2 of Turnout A - Throat - diverging A (optional)
6185    private SignalHead a33WayHead = null; //saved in A3 of Turnout A - Throat - diverging B (optional)
6186    private SignalHead b3WayHead = null;  //saved in C1 of Turnout A - at diverging A
6187    private SignalHead c3WayHead = null;  //saved in B1 of Turnout B - at continuing
6188    private SignalHead d3WayHead = null;  //saved in C1 of Turnout B - at diverging B
6189
6190    public void setSignalsAt3WayTurnoutFromMenu(
6191            @Nonnull String aName, @Nonnull String bName,
6192            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
6193        Turnout ta = InstanceManager.getDefault(jmri.TurnoutManager.class).getTurnout(aName);
6194        Turnout tb = InstanceManager.getDefault(jmri.TurnoutManager.class).getTurnout(bName);
6195        turnoutAComboBox.setSelectedItem(ta);
6196        turnoutBComboBox.setSelectedItem(tb);
6197        a1_3WaySignalHeadComboBox.setSelectedItem(null);
6198        a2_3WaySignalHeadComboBox.setSelectedItem(null);
6199        a3_3WaySignalHeadComboBox.setSelectedItem(null);
6200        b_3WaySignalHeadComboBox.setSelectedItem(null);
6201        c_3WaySignalHeadComboBox.setSelectedItem(null);
6202        d_3WaySignalHeadComboBox.setSelectedItem(null);
6203        setSignalsAt3WayTurnoutFromMenuFlag = true;
6204        setSignalsAt3WayTurnout(theEditor, theFrame);
6205        setSignalsAt3WayTurnoutFromMenuFlag = false;
6206    }
6207
6208    public void setSignalsAt3WayTurnout(@Nonnull MultiIconEditor theEditor,
6209            @Nonnull JFrame theFrame) {
6210        signalIconEditor = theEditor;
6211        signalFrame = theFrame;
6212
6213        //Initialize if needed
6214        if (setSignalsAt3WayTurnoutFrame == null) {
6215            setSignalsAt3WayTurnoutOpenFlag = false;
6216            setSignalsAt3WayTurnoutFrame = new JmriJFrame(Bundle.getMessage("SignalsAt3WayTurnout"), false, true);
6217            oneFrameToRuleThemAll(setSignalsAt3WayTurnoutFrame);
6218            setSignalsAt3WayTurnoutFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
6219            setSignalsAt3WayTurnoutFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAt3WayTurnout", true);
6220            setSignalsAt3WayTurnoutFrame.setLocation(70, 30);
6221            Container theContentPane = setSignalsAt3WayTurnoutFrame.getContentPane();
6222            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
6223
6224            JPanel panel1A = new JPanel(new FlowLayout());
6225            turnoutANameLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("TurnoutAName")));
6226            panel1A.add(turnoutANameLabel);
6227            panel1A.add(turnoutAComboBox);
6228            turnoutAComboBox.setToolTipText(Bundle.getMessage("SignalsTurnoutNameHint"));
6229            theContentPane.add(panel1A);
6230
6231            JPanel panel1B = new JPanel(new FlowLayout());
6232            turnoutBNameLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("TurnoutBName")));
6233            panel1B.add(turnoutBNameLabel);
6234            panel1B.add(turnoutBComboBox);
6235            turnoutBComboBox.setToolTipText(Bundle.getMessage("SignalsTurnoutNameHint"));
6236            theContentPane.add(panel1B);
6237            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
6238            //Provide for retrieval of names of previously saved signal heads
6239
6240            JPanel panel2 = new JPanel(new FlowLayout());
6241            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
6242            panel2.add(shTitle);
6243            panel2.add(new JLabel("   "));
6244            panel2.add(getSaved3WaySignalHeads = new JButton(Bundle.getMessage("GetSaved")));
6245            getSaved3WaySignalHeads.addActionListener(this::getSaved3WaySignals);
6246            getSaved3WaySignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
6247            theContentPane.add(panel2);
6248
6249            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
6250            JPanel panel2a = new JPanel(new FlowLayout());
6251            panel2a.add(new JLabel("   "));
6252            panel2a.add(setPlaceAllHeads);
6253            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
6254            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
6255                boolean isSelected = setPlaceAllHeads.isSelected();
6256                //(de)select all checkboxes
6257                setA13WayHead.setSelected(isSelected);
6258                setA23WayHead.setSelected(isSelected);
6259                setA33WayHead.setSelected(isSelected);
6260                setB3WayHead.setSelected(isSelected);
6261                setC3WayHead.setSelected(isSelected);
6262                setD3WayHead.setSelected(isSelected);
6263            });
6264            panel2a.add(new JLabel("  "));
6265            panel2a.add(setupAllLogic);
6266            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
6267            setupAllLogic.addActionListener((ActionEvent e) -> {
6268                boolean isSelected = setupAllLogic.isSelected();
6269                //(de)select all checkboxes
6270                setupA13WayLogic.setSelected(isSelected);
6271                setupA23WayLogic.setSelected(isSelected);
6272                setupA33WayLogic.setSelected(isSelected);
6273                setupB3WayLogic.setSelected(isSelected);
6274                setupC3WayLogic.setSelected(isSelected);
6275                setupD3WayLogic.setSelected(isSelected);
6276            });
6277            theContentPane.add(panel2a);
6278            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
6279
6280            //Signal heads located at turnout A
6281            JPanel panel20 = new JPanel(new FlowLayout());
6282            panel20.add(new JLabel(Bundle.getMessage("SignalLocated")
6283                    + " " + Bundle.getMessage("BeanNameTurnout") + " A "));
6284            theContentPane.add(panel20);
6285
6286            JPanel panel21 = new JPanel(new FlowLayout());
6287            panel21.add(new JLabel(Bundle.getMessage("MakeLabel",
6288                    throatString + " - "
6289                    + continuingString)));
6290            panel21.add(a1_3WaySignalHeadComboBox);
6291            a1_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6292            theContentPane.add(panel21);
6293
6294            JPanel panel22 = new JPanel(new FlowLayout());
6295            panel22.add(new JLabel("   "));
6296            panel22.add(setA13WayHead);
6297            setA13WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6298            panel22.add(new JLabel("  "));
6299            panel22.add(setupA13WayLogic);
6300            setupA13WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6301            theContentPane.add(panel22);
6302
6303            JPanel panel23 = new JPanel(new FlowLayout());
6304            panel23.add(new JLabel(Bundle.getMessage("MakeLabel",
6305                    throatString + " - "
6306                    + divergingAString)));
6307            panel23.add(a2_3WaySignalHeadComboBox);
6308            a2_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6309            theContentPane.add(panel23);
6310
6311            JPanel panel24 = new JPanel(new FlowLayout());
6312            panel24.add(new JLabel("   "));
6313            panel24.add(setA23WayHead);
6314            setA23WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6315            panel24.add(new JLabel("  "));
6316            panel24.add(setupA23WayLogic);
6317            setupA23WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6318            theContentPane.add(panel24);
6319
6320            JPanel panel25 = new JPanel(new FlowLayout());
6321            panel25.add(new JLabel(Bundle.getMessage("MakeLabel",
6322                    throatString + " - "
6323                    + divergingBString)));
6324            panel25.add(a3_3WaySignalHeadComboBox);
6325            a3_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6326            theContentPane.add(panel25);
6327
6328            JPanel panel26 = new JPanel(new FlowLayout());
6329            panel26.add(new JLabel("   "));
6330            panel26.add(setA33WayHead);
6331            setA33WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6332            panel26.add(new JLabel("  "));
6333            panel26.add(setupA33WayLogic);
6334            setupA33WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6335            theContentPane.add(panel26);
6336
6337            JPanel panel31 = new JPanel(new FlowLayout());
6338            panel31.add(new JLabel(Bundle.getMessage("MakeLabel",
6339                    divergingBString)));
6340            panel31.add(b_3WaySignalHeadComboBox);
6341            b_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6342            theContentPane.add(panel31);
6343
6344            JPanel panel32 = new JPanel(new FlowLayout());
6345            panel32.add(new JLabel("   "));
6346            panel32.add(setB3WayHead);
6347            setB3WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6348            panel32.add(new JLabel("  "));
6349            panel32.add(setupB3WayLogic);
6350            setupB3WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6351            theContentPane.add(panel32);
6352            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
6353            //Signal heads located at turnout B
6354
6355            JPanel panel40 = new JPanel(new FlowLayout());
6356            panel40.add(new JLabel(Bundle.getMessage("SignalLocated")
6357                    + " " + Bundle.getMessage("BeanNameTurnout") + " B "));
6358            theContentPane.add(panel40);
6359
6360            JPanel panel41 = new JPanel(new FlowLayout());
6361            panel41.add(new JLabel(Bundle.getMessage("MakeLabel",
6362                    continuingString)));
6363            panel41.add(c_3WaySignalHeadComboBox);
6364            c_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6365            theContentPane.add(panel41);
6366
6367            JPanel panel42 = new JPanel(new FlowLayout());
6368            panel42.add(new JLabel("   "));
6369            panel42.add(setC3WayHead);
6370            setC3WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6371            panel42.add(new JLabel("  "));
6372            panel42.add(setupC3WayLogic);
6373            setupC3WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6374            theContentPane.add(panel42);
6375
6376            JPanel panel43 = new JPanel(new FlowLayout());
6377            panel43.add(new JLabel(Bundle.getMessage("MakeLabel",
6378                    divergingBString)));
6379            panel43.add(d_3WaySignalHeadComboBox);
6380            d_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6381            theContentPane.add(panel43);
6382
6383            JPanel panel44 = new JPanel(new FlowLayout());
6384            panel44.add(new JLabel("   "));
6385            panel44.add(setD3WayHead);
6386            setD3WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6387            panel44.add(new JLabel("  "));
6388            panel44.add(setupD3WayLogic);
6389            setupD3WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6390            theContentPane.add(panel44);
6391            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
6392            //buttons
6393
6394            JPanel panel6 = new JPanel(new FlowLayout());
6395            panel6.add(change3WaySignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
6396            change3WaySignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
6397            change3WaySignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
6398            panel6.add(new JLabel("   "));
6399            panel6.add(set3WaySignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
6400            set3WaySignalsDone.addActionListener(this::set3WaySignalsDonePressed);
6401            set3WaySignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
6402
6403            panel6.add(set3WaySignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
6404            set3WaySignalsCancel.addActionListener(this::set3WaySignalsCancelPressed);
6405            set3WaySignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
6406            theContentPane.add(panel6);
6407
6408            //make this button the default button (return or enter activates)
6409            JRootPane rootPane = SwingUtilities.getRootPane(set3WaySignalsDone);
6410            if (rootPane != null) {
6411                rootPane.setDefaultButton(set3WaySignalsDone);
6412            }
6413
6414            setSignalsAt3WayTurnoutFrame.addWindowListener(new WindowAdapter() {
6415                @Override
6416                public void windowClosing(WindowEvent e) {
6417                    set3WaySignalsCancelPressed(null);
6418                }
6419            });
6420        }
6421        setPlaceAllHeads.setSelected(false);
6422        setupAllLogic.setSelected(false);
6423
6424        turnoutAComboBox.setVisible(!setSignalsAt3WayTurnoutFromMenuFlag);
6425        turnoutBComboBox.setVisible(!setSignalsAt3WayTurnoutFromMenuFlag);
6426        if (setSignalsAt3WayTurnoutFromMenuFlag) {
6427            turnoutANameLabel.setText(Bundle.getMessage("MakeLabel",
6428                    Bundle.getMessage("BeanNameTurnout") + " A")
6429                    + turnoutAComboBox.getSelectedItemDisplayName());
6430            turnoutBNameLabel.setText(Bundle.getMessage("MakeLabel",
6431                    Bundle.getMessage("BeanNameTurnout") + " B")
6432                    + turnoutBComboBox.getSelectedItemDisplayName());
6433            getSaved3WaySignals(null);
6434        } else {
6435            turnoutANameLabel.setText(Bundle.getMessage("MakeLabel",
6436                    Bundle.getMessage("TurnoutAName")));
6437            turnoutBNameLabel.setText(Bundle.getMessage("MakeLabel",
6438                    Bundle.getMessage("TurnoutBName")));
6439        }
6440
6441        if (!setSignalsAt3WayTurnoutOpenFlag) {
6442            setSignalsAt3WayTurnoutFrame.setPreferredSize(null);
6443            setSignalsAt3WayTurnoutFrame.pack();
6444            setSignalsAt3WayTurnoutOpenFlag = true;
6445        }
6446        setSignalsAt3WayTurnoutFrame.setVisible(true);
6447    }   //setSignalsAt3WayTurnout
6448
6449    private void getSaved3WaySignals(ActionEvent a) {
6450        if (!get3WayTurnoutInformation()) {
6451            return;
6452        }
6453        a1_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutA.getSignalA1());
6454        a2_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutA.getSignalA2());
6455        a3_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutA.getSignalA3());
6456        b_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutA.getSignalC1());
6457        c_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutB.getSignalB1());
6458        d_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutB.getSignalC1());
6459    }
6460
6461    private void set3WaySignalsCancelPressed(ActionEvent a) {
6462        setSignalsAt3WayTurnoutOpenFlag = false;
6463        setSignalsAt3WayTurnoutFrame.setVisible(false);
6464    }
6465
6466    private boolean get3WayTurnoutInformation() {
6467        HitPointType type = HitPointType.NONE;
6468        Object connect = null;
6469        turnoutA = null;
6470        turnoutB = null;
6471        layoutTurnoutA = null;
6472        layoutTurnoutB = null;
6473
6474        String str = turnoutAComboBox.getSelectedItemDisplayName();
6475        if ((str == null) || str.isEmpty()) {
6476            //turnout A not entered, test turnout B
6477            str = turnoutBComboBox.getSelectedItemDisplayName();
6478            if ((str == null) || str.isEmpty()) {
6479                //no entries in turnout fields
6480                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6481                        Bundle.getMessage("SignalsError1"),
6482                        Bundle.getMessage("ErrorTitle"),
6483                        JOptionPane.ERROR_MESSAGE);
6484                return false;
6485            }
6486            turnoutB = InstanceManager.turnoutManagerInstance().getTurnout(str);
6487            if (turnoutB == null) {
6488                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6489                        Bundle.getMessage("SignalsError2",
6490                                new Object[]{str}), Bundle.getMessage("ErrorTitle"),
6491                        JOptionPane.ERROR_MESSAGE);
6492                return false;
6493            }
6494            String uname = turnoutB.getUserName();
6495            if ((uname == null) || uname.isEmpty()
6496                    || !uname.equals(str)) {
6497                turnoutBComboBox.setSelectedItem(turnoutB);
6498            }
6499            layoutTurnoutB = getLayoutTurnoutFromTurnout(turnoutB, false, str, setSignalsAt3WayTurnoutFrame);
6500            if (layoutTurnoutB == null) {
6501                return false;
6502            }
6503            //have turnout B and layout turnout B - look for turnout A
6504            connectorTrack = (TrackSegment) layoutTurnoutB.getConnectA();
6505            if (connectorTrack == null) {
6506                //Inform user of error, and terminate
6507                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6508                        Bundle.getMessage("SignalsError19"),
6509                        Bundle.getMessage("ErrorTitle"),
6510                        JOptionPane.ERROR_MESSAGE);
6511                return false;
6512            }
6513            type = connectorTrack.getType1();
6514            connect = connectorTrack.getConnect1();
6515            if (connect == layoutTurnoutB) {
6516                type = connectorTrack.getType2();
6517                connect = connectorTrack.getConnect2();
6518            }
6519            if ((type != HitPointType.TURNOUT_B) || (connect == null)) {
6520                //Not two turnouts connected as required by a single Track Segment
6521                //Inform user of error and terminate
6522                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6523                        Bundle.getMessage("SignalsError19"),
6524                        Bundle.getMessage("ErrorTitle"),
6525                        JOptionPane.ERROR_MESSAGE);
6526                return false;
6527            }
6528            layoutTurnoutA = (LayoutTurnout) connect;
6529            turnoutA = layoutTurnoutA.getTurnout();
6530            if (turnoutA == null) {
6531                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6532                        Bundle.getMessage("SignalsError19"),
6533                        Bundle.getMessage("ErrorTitle"),
6534                        JOptionPane.ERROR_MESSAGE);
6535                return false;
6536            }
6537            turnoutAComboBox.setSelectedItem(turnoutA);
6538        } else {
6539            //something was entered in the turnout A field
6540            turnoutA = InstanceManager.turnoutManagerInstance().getTurnout(str);
6541            if (turnoutA == null) {
6542                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6543                        Bundle.getMessage("SignalsError2",
6544                                new Object[]{str}), Bundle.getMessage("ErrorTitle"),
6545                        JOptionPane.ERROR_MESSAGE);
6546                return false;
6547            }
6548            String uname = turnoutA.getUserName();
6549            if ((uname == null) || uname.isEmpty()
6550                    || !uname.equals(str)) {
6551                turnoutAComboBox.setSelectedItem(turnoutA);
6552            }
6553            //have turnout A - get corresponding layoutTurnout
6554            layoutTurnoutA = getLayoutTurnoutFromTurnout(turnoutA, false, str, setSignalsAt3WayTurnoutFrame);
6555            if (layoutTurnoutA == null) {
6556                return false;
6557            }
6558            turnoutAComboBox.setSelectedItem(layoutTurnoutA.getTurnout());
6559            //have turnout A and layout turnout A - was something entered for turnout B
6560            str = turnoutBComboBox.getSelectedItemDisplayName();
6561            if ((str == null) || str.isEmpty()) {
6562                //no entry for turnout B
6563                connectorTrack = (TrackSegment) layoutTurnoutA.getConnectB();
6564                if (connectorTrack == null) {
6565                    //Inform user of error, and terminate
6566                    JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6567                            Bundle.getMessage("SignalsError19"),
6568                            Bundle.getMessage("ErrorTitle"),
6569                            JOptionPane.ERROR_MESSAGE);
6570                    return false;
6571                }
6572                type = connectorTrack.getType1();
6573                connect = connectorTrack.getConnect1();
6574                if (connect == layoutTurnoutA) {
6575                    type = connectorTrack.getType2();
6576                    connect = connectorTrack.getConnect2();
6577                }
6578                if ((type != HitPointType.TURNOUT_A) || (connect == null)) {
6579                    // Not two turnouts connected with the throat of B connected to the continuing of A
6580                    //  by a single Track Segment.  Inform user of error and terminate.
6581                    JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6582                            Bundle.getMessage("SignalsError19"),
6583                            Bundle.getMessage("ErrorTitle"),
6584                            JOptionPane.ERROR_MESSAGE);
6585                    return false;
6586                }
6587                layoutTurnoutB = (LayoutTurnout) connect;
6588                turnoutB = layoutTurnoutB.getTurnout();
6589                if (turnoutB == null) {
6590                    JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6591                            Bundle.getMessage("SignalsError19"),
6592                            Bundle.getMessage("ErrorTitle"),
6593                            JOptionPane.ERROR_MESSAGE);
6594                    return false;
6595                }
6596                turnoutBComboBox.setSelectedItem(turnoutB);
6597            } else {
6598                //turnout B entered also
6599                turnoutB = InstanceManager.turnoutManagerInstance().getTurnout(str);
6600                if (turnoutB == null) {
6601                    JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6602                            Bundle.getMessage("SignalsError2",
6603                                    new Object[]{str}), Bundle.getMessage("ErrorTitle"),
6604                            JOptionPane.ERROR_MESSAGE);
6605                    return false;
6606                }
6607                uname = turnoutB.getUserName();
6608                if ((uname == null) || uname.isEmpty()
6609                        || !uname.equals(str)) {
6610                    turnoutBComboBox.setSelectedItem(turnoutB);
6611                }
6612                layoutTurnoutB = getLayoutTurnoutFromTurnout(turnoutB, false, str, setSignalsAt3WayTurnoutFrame);
6613                if (layoutTurnoutB == null) {
6614                    return false;
6615                }
6616                turnoutBComboBox.setSelectedItem(layoutTurnoutB.getTurnout());
6617                //check that layout turnout A and layout turnout B are connected as required
6618                if (layoutTurnoutA.getConnectB() != layoutTurnoutB.getConnectA()) {
6619                    //Not two turnouts connected as required by a single Track Segment
6620                    //Inform user of error and terminate
6621                    JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6622                            Bundle.getMessage("SignalsError19"),
6623                            Bundle.getMessage("ErrorTitle"),
6624                            JOptionPane.ERROR_MESSAGE);
6625                    return false;
6626                }
6627                connectorTrack = (TrackSegment) layoutTurnoutA.getConnectB();
6628            }
6629        }
6630        return true;
6631    }   //get3WayTurnoutInformation
6632
6633    private void set3WaySignalsDonePressed(ActionEvent a) {
6634        //process turnout names
6635        if (!get3WayTurnoutInformation()) {
6636            return;
6637        }
6638        //process signal head names
6639        if (!get3WaySignalHeadInformation()) {
6640            return;
6641        }
6642        //place signals as requested at turnout A
6643        String signalHeadName = a1_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6644        if (signalHeadName == null) {
6645            signalHeadName = "";
6646        }
6647        if (setA13WayHead.isSelected()) {
6648            if (isHeadOnPanel(a13WayHead)
6649                    && (a13WayHead != getHeadFromName(layoutTurnoutA.getSignalA1Name()))) {
6650                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6651                        Bundle.getMessage("SignalsError6",
6652                                new Object[]{signalHeadName}),
6653                        Bundle.getMessage("ErrorTitle"),
6654                        JOptionPane.ERROR_MESSAGE);
6655                return;
6656            } else {
6657                removeSignalHeadFromPanel(layoutTurnoutA.getSignalA1Name());
6658                place3WayThroatContinuing();
6659                removeAssignment(a13WayHead);
6660                layoutTurnoutA.setSignalA1Name(signalHeadName);
6661                needRedraw = true;
6662            }
6663        } else {
6664            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a13WayHead, layoutTurnoutA);
6665            if (assigned == LayoutTurnout.Geometry.NONE) {
6666                if (isHeadOnPanel(a13WayHead)
6667                        && isHeadAssignedAnywhere(a13WayHead)) {
6668                    JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6669                            Bundle.getMessage("SignalsError8",
6670                                    new Object[]{signalHeadName}),
6671                            Bundle.getMessage("ErrorTitle"),
6672                            JOptionPane.ERROR_MESSAGE);
6673                    return;
6674                } else {
6675                    removeSignalHeadFromPanel(layoutTurnoutA.getSignalA1Name());
6676                    removeAssignment(a13WayHead);
6677                    layoutTurnoutA.setSignalA1Name(signalHeadName);
6678                }
6679                //} else if (assigned != A1) {
6680                //need to figure out what to do in this case.
6681            }
6682        }
6683
6684        signalHeadName = a2_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6685        if (signalHeadName == null) {
6686            signalHeadName = "";
6687        }
6688        if ((setA23WayHead.isSelected()) && (a23WayHead != null)) {
6689            if (isHeadOnPanel(a23WayHead)
6690                    && (a23WayHead != getHeadFromName(layoutTurnoutA.getSignalA2Name()))) {
6691                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6692                        Bundle.getMessage("SignalsError6",
6693                                new Object[]{signalHeadName}),
6694                        Bundle.getMessage("ErrorTitle"),
6695                        JOptionPane.ERROR_MESSAGE);
6696                return;
6697            } else {
6698                removeSignalHeadFromPanel(layoutTurnoutA.getSignalA2Name());
6699                place3WayThroatDivergingA();
6700                removeAssignment(a23WayHead);
6701                layoutTurnoutA.setSignalA2Name(signalHeadName);
6702                needRedraw = true;
6703            }
6704        } else if (a23WayHead != null) {
6705            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a23WayHead, layoutTurnoutA);
6706            if (assigned == LayoutTurnout.Geometry.NONE) {
6707                if (isHeadOnPanel(a23WayHead)
6708                        && isHeadAssignedAnywhere(a23WayHead)) {
6709                    JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6710                            Bundle.getMessage("SignalsError8",
6711                                    new Object[]{signalHeadName}),
6712                            Bundle.getMessage("ErrorTitle"),
6713                            JOptionPane.ERROR_MESSAGE);
6714                    return;
6715                } else {
6716                    removeSignalHeadFromPanel(layoutTurnoutA.getSignalA2Name());
6717                    removeAssignment(a23WayHead);
6718                    layoutTurnoutA.setSignalA2Name(signalHeadName);
6719                }
6720                //} else if (assigned != A2) {
6721                //need to figure out what to do in this case.
6722            }
6723        } else {  //a23WayHead is always null here
6724            removeSignalHeadFromPanel(layoutTurnoutA.getSignalA2Name());
6725            layoutTurnoutA.setSignalA2Name("");
6726        }
6727
6728        signalHeadName = a3_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6729        if (signalHeadName == null) {
6730            signalHeadName = "";
6731        }
6732        if ((setA33WayHead.isSelected()) && (a33WayHead != null)) {
6733            if (isHeadOnPanel(a33WayHead)
6734                    && (a33WayHead != getHeadFromName(layoutTurnoutA.getSignalA3Name()))) {
6735                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6736                        Bundle.getMessage("SignalsError6",
6737                                new Object[]{signalHeadName}),
6738                        Bundle.getMessage("ErrorTitle"),
6739                        JOptionPane.ERROR_MESSAGE);
6740                return;
6741            } else {
6742                removeSignalHeadFromPanel(layoutTurnoutA.getSignalA3Name());
6743                place3WayThroatDivergingB();
6744                removeAssignment(a33WayHead);
6745                layoutTurnoutA.setSignalA3Name(signalHeadName);
6746                needRedraw = true;
6747            }
6748        } else if (a33WayHead != null) {
6749            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a33WayHead, layoutTurnoutA);
6750            if (assigned == LayoutTurnout.Geometry.NONE) {
6751                if (isHeadOnPanel(a33WayHead)
6752                        && isHeadAssignedAnywhere(a33WayHead)) {
6753                    JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6754                            Bundle.getMessage("SignalsError8",
6755                                    new Object[]{signalHeadName}),
6756                            Bundle.getMessage("ErrorTitle"),
6757                            JOptionPane.ERROR_MESSAGE);
6758                    return;
6759                } else {
6760                    removeSignalHeadFromPanel(layoutTurnoutA.getSignalA3Name());
6761                    removeAssignment(a33WayHead);
6762                    layoutTurnoutA.setSignalA3Name(signalHeadName);
6763                }
6764                //} else if (assigned != A3) {
6765                //need to figure out what to do in this case.
6766            }
6767        } else {  //a23WayHead is always null here
6768            removeSignalHeadFromPanel(layoutTurnoutA.getSignalA3Name());
6769            layoutTurnoutA.setSignalA3Name("");
6770        }
6771
6772        signalHeadName = b_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6773        if (signalHeadName == null) {
6774            signalHeadName = "";
6775        }
6776        if (setB3WayHead.isSelected()) {
6777            if (isHeadOnPanel(b3WayHead)
6778                    && (b3WayHead != getHeadFromName(layoutTurnoutA.getSignalC1Name()))) {
6779                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6780                        Bundle.getMessage("SignalsError6",
6781                                new Object[]{signalHeadName}),
6782                        Bundle.getMessage("ErrorTitle"),
6783                        JOptionPane.ERROR_MESSAGE);
6784                return;
6785            } else {
6786                removeSignalHeadFromPanel(layoutTurnoutA.getSignalC1Name());
6787                place3WayDivergingA();
6788                removeAssignment(b3WayHead);
6789                layoutTurnoutA.setSignalC1Name(signalHeadName);
6790                needRedraw = true;
6791            }
6792        } else {
6793            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b3WayHead, layoutTurnoutA);
6794            if (assigned == LayoutTurnout.Geometry.NONE) {
6795                if (isHeadOnPanel(b3WayHead)
6796                        && isHeadAssignedAnywhere(b3WayHead)) {
6797                    JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6798                            Bundle.getMessage("SignalsError8",
6799                                    new Object[]{signalHeadName}),
6800                            Bundle.getMessage("ErrorTitle"),
6801                            JOptionPane.ERROR_MESSAGE);
6802                    return;
6803                } else {
6804                    removeSignalHeadFromPanel(layoutTurnoutA.getSignalC1Name());
6805                    removeAssignment(b3WayHead);
6806                    layoutTurnoutA.setSignalC1Name(signalHeadName);
6807                }
6808                //} else if (assigned != A1) {
6809                //need to figure out what to do in this case.
6810            }
6811        }
6812
6813        //place signals as requested at Turnout C
6814        signalHeadName = c_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6815        if (signalHeadName == null) {
6816            signalHeadName = "";
6817        }
6818        if (setC3WayHead.isSelected()) {
6819            if (isHeadOnPanel(c3WayHead)
6820                    && (c3WayHead != getHeadFromName(layoutTurnoutB.getSignalB1Name()))) {
6821                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6822                        Bundle.getMessage("SignalsError6",
6823                                new Object[]{signalHeadName}),
6824                        Bundle.getMessage("ErrorTitle"),
6825                        JOptionPane.ERROR_MESSAGE);
6826                return;
6827            } else {
6828                removeSignalHeadFromPanel(layoutTurnoutB.getSignalB1Name());
6829                place3WayContinuing();
6830                removeAssignment(c3WayHead);
6831                layoutTurnoutB.setSignalB1Name(signalHeadName);
6832                needRedraw = true;
6833            }
6834        } else {
6835            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c3WayHead, layoutTurnoutB);
6836            if (assigned == LayoutTurnout.Geometry.NONE) {
6837                if (isHeadOnPanel(c3WayHead)
6838                        && isHeadAssignedAnywhere(c3WayHead)) {
6839                    JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6840                            Bundle.getMessage("SignalsError8",
6841                                    new Object[]{signalHeadName}),
6842                            Bundle.getMessage("ErrorTitle"),
6843                            JOptionPane.ERROR_MESSAGE);
6844                    return;
6845                } else {
6846                    removeSignalHeadFromPanel(layoutTurnoutB.getSignalB1Name());
6847                    removeAssignment(c3WayHead);
6848                    layoutTurnoutB.setSignalB1Name(signalHeadName);
6849                }
6850                //} else if (assigned != B1) {
6851                //need to figure out what to do in this case.
6852            }
6853        }
6854
6855        signalHeadName = d_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6856        if (signalHeadName == null) {
6857            signalHeadName = "";
6858        }
6859        if (setD3WayHead.isSelected()) {
6860            if (isHeadOnPanel(d3WayHead)
6861                    && (d3WayHead != getHeadFromName(layoutTurnoutB.getSignalC1Name()))) {
6862                JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6863                        Bundle.getMessage("SignalsError6",
6864                                new Object[]{signalHeadName}),
6865                        Bundle.getMessage("ErrorTitle"),
6866                        JOptionPane.ERROR_MESSAGE);
6867                return;
6868            } else {
6869                removeSignalHeadFromPanel(layoutTurnoutB.getSignalC1Name());
6870                place3WayDivergingB();
6871                removeAssignment(d3WayHead);
6872                layoutTurnoutB.setSignalC1Name(signalHeadName);
6873                needRedraw = true;
6874            }
6875        } else {
6876            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d3WayHead, layoutTurnoutB);
6877            if (assigned == LayoutTurnout.Geometry.NONE) {
6878                if (isHeadOnPanel(d3WayHead)
6879                        && isHeadAssignedAnywhere(d3WayHead)) {
6880                    JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6881                            Bundle.getMessage("SignalsError8",
6882                                    new Object[]{signalHeadName}),
6883                            Bundle.getMessage("ErrorTitle"),
6884                            JOptionPane.ERROR_MESSAGE);
6885                    return;
6886                } else {
6887                    removeSignalHeadFromPanel(layoutTurnoutB.getSignalC1Name());
6888                    removeAssignment(d3WayHead);
6889                    layoutTurnoutB.setSignalC1Name(signalHeadName);
6890                }
6891                //} else if (assigned != C1) {
6892                //need to figure out what to do in this case.
6893            }
6894        }
6895        //setup Logic if requested and enough information is available
6896        if (setupA13WayLogic.isSelected()) {
6897            set3WayLogicThroatContinuing();
6898        }
6899        if ((a23WayHead != null) && setupA23WayLogic.isSelected()) {
6900            set3WayLogicThroatDivergingA();
6901        }
6902        if ((a33WayHead != null) && setupA33WayLogic.isSelected()) {
6903            set3WayLogicThroatDivergingB();
6904        }
6905        if (setupB3WayLogic.isSelected()) {
6906            set3WayLogicDivergingA();
6907        }
6908        if (setupC3WayLogic.isSelected()) {
6909            set3WayLogicContinuing();
6910        }
6911        if (setupD3WayLogic.isSelected()) {
6912            set3WayLogicDivergingB();
6913        }
6914        //link the two turnouts
6915        signalHeadName = turnoutBComboBox.getSelectedItemDisplayName();
6916        if (signalHeadName == null) {
6917            signalHeadName = "";
6918        }
6919        layoutTurnoutA.setLinkedTurnoutName(signalHeadName);
6920        layoutTurnoutA.setLinkType(LayoutTurnout.LinkType.FIRST_3_WAY);
6921        signalHeadName = turnoutAComboBox.getSelectedItemDisplayName();
6922        if (signalHeadName == null) {
6923            signalHeadName = "";
6924        }
6925        layoutTurnoutB.setLinkedTurnoutName(signalHeadName);
6926        layoutTurnoutB.setLinkType(LayoutTurnout.LinkType.SECOND_3_WAY);
6927        //finish up
6928        setSignalsAt3WayTurnoutOpenFlag = false;
6929        setSignalsAt3WayTurnoutFrame.setVisible(false);
6930        if (needRedraw) {
6931            layoutEditor.redrawPanel();
6932            needRedraw = false;
6933            layoutEditor.setDirty();
6934        }
6935    }   //set3WaySignalsDonePressed
6936
6937    private boolean get3WaySignalHeadInformation() {
6938        a13WayHead = getSignalHeadFromEntry(a1_3WaySignalHeadComboBox, true, setSignalsAt3WayTurnoutFrame);
6939        if (a13WayHead == null) {
6940            return false;
6941        }
6942        a23WayHead = getSignalHeadFromEntry(a2_3WaySignalHeadComboBox, false, setSignalsAt3WayTurnoutFrame);
6943        a33WayHead = getSignalHeadFromEntry(a3_3WaySignalHeadComboBox, false, setSignalsAt3WayTurnoutFrame);
6944        if (((a23WayHead == null) && (a33WayHead != null)) || ((a33WayHead == null)
6945                && (a23WayHead != null))) {
6946            return false;
6947        }
6948        b3WayHead = getSignalHeadFromEntry(b_3WaySignalHeadComboBox, true, setSignalsAt3WayTurnoutFrame);
6949        if (b3WayHead == null) {
6950            return false;
6951        }
6952        c3WayHead = getSignalHeadFromEntry(c_3WaySignalHeadComboBox, true, setSignalsAt3WayTurnoutFrame);
6953        if (c3WayHead == null) {
6954            return false;
6955        }
6956        d3WayHead = getSignalHeadFromEntry(d_3WaySignalHeadComboBox, true, setSignalsAt3WayTurnoutFrame);
6957        if (d3WayHead == null) {
6958            return false;
6959        }
6960        return true;
6961    }
6962
6963    private void place3WayThroatContinuing() {
6964        if (testIcon == null) {
6965            testIcon = signalIconEditor.getIcon(0);
6966        }
6967        String signalHeadName = a1_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6968        if (signalHeadName == null) {
6969            signalHeadName = "";
6970        }
6971        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
6972
6973        LayoutTurnoutView layoutTurnoutAView = layoutEditor.getLayoutTurnoutView(layoutTurnoutA);
6974        Point2D coordsA = layoutTurnoutAView.getCoordsA();
6975        Point2D coordsCenter = layoutTurnoutAView.getCoordsCenter();
6976
6977        double aDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsA, coordsCenter));
6978        Point2D delta = new Point2D.Double(-shift, -shift);
6979
6980        delta = MathUtil.rotateDEG(delta, aDirDEG);
6981        Point2D where = MathUtil.add(coordsA, delta);
6982        setSignalHeadOnPanel(aDirDEG, signalHeadName, where);
6983    }
6984
6985    private void place3WayThroatDivergingA() {
6986        if (testIcon == null) {
6987            testIcon = signalIconEditor.getIcon(0);
6988        }
6989        String signalHeadName = a2_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6990        if (signalHeadName == null) {
6991            signalHeadName = "";
6992        }
6993        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
6994
6995        LayoutTurnoutView layoutTurnoutAView = layoutEditor.getLayoutTurnoutView(layoutTurnoutA);
6996        Point2D coordsA = layoutTurnoutAView.getCoordsA();
6997        Point2D coordsCenter = layoutTurnoutAView.getCoordsCenter();
6998
6999        double aDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsA, coordsCenter));
7000        Point2D delta = new Point2D.Double(+shift, -shift);
7001
7002        delta = MathUtil.rotateDEG(delta, aDirDEG);
7003        Point2D where = MathUtil.add(coordsA, delta);
7004        setSignalHeadOnPanel(aDirDEG, signalHeadName, where);
7005    }
7006
7007    private void place3WayThroatDivergingB() {
7008        if (testIcon == null) {
7009            testIcon = signalIconEditor.getIcon(0);
7010        }
7011        String signalHeadName = a3_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7012        if (signalHeadName == null) {
7013            signalHeadName = "";
7014        }
7015        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
7016
7017        LayoutTurnoutView layoutTurnoutAView = layoutEditor.getLayoutTurnoutView(layoutTurnoutA);
7018        Point2D coordsA = layoutTurnoutAView.getCoordsA();
7019        Point2D coordsCenter = layoutTurnoutAView.getCoordsCenter();
7020
7021        double aDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsA, coordsCenter));
7022        Point2D delta = new Point2D.Double(+3.0 * shift, -shift);
7023
7024        delta = MathUtil.rotateDEG(delta, aDirDEG);
7025        Point2D where = MathUtil.add(coordsA, delta);
7026        setSignalHeadOnPanel(aDirDEG, signalHeadName, where);
7027    }
7028
7029    private void place3WayDivergingA() {
7030        if (testIcon == null) {
7031            testIcon = signalIconEditor.getIcon(0);
7032        }
7033        String signalHeadName = b_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7034        if (signalHeadName == null) {
7035            signalHeadName = "";
7036        }
7037        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
7038
7039        LayoutTurnoutView layoutTurnoutAView = layoutEditor.getLayoutTurnoutView(layoutTurnoutA);
7040        Point2D coordsB = layoutTurnoutAView.getCoordsB();
7041        Point2D coordsC = layoutTurnoutAView.getCoordsC();
7042        Point2D coordsCenter = layoutTurnoutAView.getCoordsCenter();
7043
7044        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
7045        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
7046        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
7047        double shiftX = shift;
7048        if (diffDirDEG >= 0.0) {
7049            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
7050        }
7051        Point2D delta = new Point2D.Double(shiftX, -shift);
7052
7053        delta = MathUtil.rotateDEG(delta, cDirDEG);
7054        Point2D where = MathUtil.add(coordsC, delta);
7055        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
7056    }
7057
7058    private void place3WayContinuing() {
7059        if (testIcon == null) {
7060            testIcon = signalIconEditor.getIcon(0);
7061        }
7062        String signalHeadName = c_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7063        if (signalHeadName == null) {
7064            signalHeadName = "";
7065        }
7066        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
7067
7068        LayoutTurnoutView layoutTurnoutBView = layoutEditor.getLayoutTurnoutView(layoutTurnoutB);
7069        Point2D coordsB = layoutTurnoutBView.getCoordsB();
7070        Point2D coordsC = layoutTurnoutBView.getCoordsC();
7071        Point2D coordsCenter = layoutTurnoutBView.getCoordsCenter();
7072
7073        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
7074        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
7075        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
7076        double shiftX = shift;
7077        if (diffDirDEG >= 0.0) {
7078            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
7079        }
7080        Point2D delta = new Point2D.Double(shiftX, -shift);
7081
7082        delta = MathUtil.rotateDEG(delta, bDirDEG);
7083        Point2D where = MathUtil.add(coordsB, delta);
7084        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
7085    }
7086
7087    private void place3WayDivergingB() {
7088        if (testIcon == null) {
7089            testIcon = signalIconEditor.getIcon(0);
7090        }
7091        String signalHeadName = d_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7092        if (signalHeadName == null) {
7093            signalHeadName = "";
7094        }
7095        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
7096
7097        LayoutTurnoutView layoutTurnoutBView = layoutEditor.getLayoutTurnoutView(layoutTurnoutB);
7098        Point2D coordsC = layoutTurnoutBView.getCoordsC();
7099        Point2D coordsB = layoutTurnoutBView.getCoordsB();
7100        Point2D coordsCenter = layoutTurnoutBView.getCoordsCenter();
7101
7102        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
7103        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
7104        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
7105        double shiftX = shift;
7106        if (diffDirDEG >= 0.0) {
7107            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
7108        }
7109        Point2D delta = new Point2D.Double(shiftX, -shift);
7110
7111        delta = MathUtil.rotateDEG(delta, cDirDEG);
7112        Point2D where = MathUtil.add(coordsC, delta);
7113        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
7114    }
7115
7116    private void set3WayLogicThroatContinuing() {
7117        TrackSegment track = (TrackSegment) layoutTurnoutB.getConnectB();
7118        if (track == null) {
7119            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7120                    Bundle.getMessage("InfoMessage7"),
7121                    Bundle.getMessage("MessageTitle"),
7122                    JOptionPane.INFORMATION_MESSAGE);
7123            return;
7124        }
7125        LayoutBlock block = track.getLayoutBlock();
7126        if (block == null) {
7127            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7128                    Bundle.getMessage("InfoMessage6"),
7129                    Bundle.getMessage("MessageTitle"),
7130                    JOptionPane.INFORMATION_MESSAGE);
7131            return;
7132        }
7133        Sensor occupancy = block.getOccupancySensor();
7134        if (occupancy == null) {
7135            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7136                    Bundle.getMessage("InfoMessage4",
7137                            new Object[]{block.getUserName()}),
7138                    Bundle.getMessage("MessageTitle"),
7139                    JOptionPane.INFORMATION_MESSAGE);
7140            return;
7141        }
7142        String signalHeadName = a1_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7143        if (signalHeadName == null) {
7144            signalHeadName = "";
7145        }
7146        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutB,
7147                signalHeadName, setSignalsAt3WayTurnoutFrame);
7148        if ((nextHead == null) && (!reachedEndBumper())) {
7149            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7150                    Bundle.getMessage("InfoMessage5",
7151                            new Object[]{block.getUserName()}),
7152                    Bundle.getMessage("MessageTitle"),
7153                    JOptionPane.INFORMATION_MESSAGE);
7154            return;
7155        }
7156        if (a23WayHead != null) {
7157            //set up logic for continuing head with 3 heads at throat
7158            if (!initializeBlockBossLogic(signalHeadName)) {
7159                return;
7160            }
7161            logic.setMode(BlockBossLogic.TRAILINGMAIN);
7162            logic.setTurnout(turnoutB.getDisplayName());
7163            logic.setSensor1(occupancy.getDisplayName());
7164            if (nextHead != null) {
7165                logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7166            }
7167            if (auxSignal != null) {
7168                logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7169            }
7170            String nearSensorName = setupNearLogix(layoutTurnoutA, true, a13WayHead);
7171            addNearSensorToLogic(nearSensorName);
7172            finalizeBlockBossLogic();
7173            return;
7174        }
7175        //only one head at the throat
7176        JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7177                Bundle.getMessage("InfoMessage9"),
7178                Bundle.getMessage("MessageTitle"),
7179                JOptionPane.INFORMATION_MESSAGE);
7180        return;
7181    }   //set3WayLogicThroatContinuing
7182
7183    private void set3WayLogicThroatDivergingA() {
7184        TrackSegment track = (TrackSegment) layoutTurnoutA.getConnectC();
7185        if (track == null) {
7186            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7187                    Bundle.getMessage("InfoMessage7"),
7188                    Bundle.getMessage("MessageTitle"),
7189                    JOptionPane.INFORMATION_MESSAGE);
7190            return;
7191        }
7192        LayoutBlock block = track.getLayoutBlock();
7193        if (block == null) {
7194            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7195                    Bundle.getMessage("InfoMessage6"),
7196                    Bundle.getMessage("MessageTitle"),
7197                    JOptionPane.INFORMATION_MESSAGE);
7198            return;
7199        }
7200        Sensor occupancy = block.getOccupancySensor();
7201        if (occupancy == null) {
7202            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7203                    Bundle.getMessage("InfoMessage4",
7204                            new Object[]{block.getUserName()}),
7205                    Bundle.getMessage("MessageTitle"),
7206                    JOptionPane.INFORMATION_MESSAGE);
7207            return;
7208        }
7209        String signalHeadName = a2_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7210        if (signalHeadName == null) {
7211            signalHeadName = "";
7212        }
7213        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutA,
7214                signalHeadName, setSignalsAt3WayTurnoutFrame);
7215        if ((nextHead == null) && (!reachedEndBumper())) {
7216            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7217                    Bundle.getMessage("InfoMessage5",
7218                            new Object[]{block.getUserName()}),
7219                    Bundle.getMessage("MessageTitle"),
7220                    JOptionPane.INFORMATION_MESSAGE);
7221            return;
7222        }
7223        if (!initializeBlockBossLogic(signalHeadName)) {
7224            return;
7225        }
7226        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
7227        logic.setTurnout(turnoutA.getDisplayName());
7228        logic.setSensor1(occupancy.getDisplayName());
7229        if (nextHead != null) {
7230            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7231        }
7232        if (auxSignal != null) {
7233            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7234        }
7235        if (!layoutTurnoutA.isMainlineC()) {
7236            logic.setLimitSpeed2(true);
7237        }
7238        finalizeBlockBossLogic();
7239    }   //set3WayLogicThroatDivergingA
7240
7241    private void set3WayLogicThroatDivergingB() {
7242        TrackSegment track = (TrackSegment) layoutTurnoutB.getConnectC();
7243        if (track == null) {
7244            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7245                    Bundle.getMessage("InfoMessage7"),
7246                    Bundle.getMessage("MessageTitle"),
7247                    JOptionPane.INFORMATION_MESSAGE);
7248            return;
7249        }
7250        LayoutBlock block = track.getLayoutBlock();
7251        if (block == null) {
7252            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7253                    Bundle.getMessage("InfoMessage6"),
7254                    Bundle.getMessage("MessageTitle"),
7255                    JOptionPane.INFORMATION_MESSAGE);
7256            return;
7257        }
7258        Sensor occupancy = block.getOccupancySensor();
7259        if (occupancy == null) {
7260            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7261                    Bundle.getMessage("InfoMessage4",
7262                            new Object[]{block.getUserName()}),
7263                    Bundle.getMessage("MessageTitle"),
7264                    JOptionPane.INFORMATION_MESSAGE);
7265            return;
7266        }
7267        String signalHeadName = a3_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7268        if (signalHeadName == null) {
7269            signalHeadName = "";
7270        }
7271        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutB,
7272                signalHeadName, setSignalsAt3WayTurnoutFrame);
7273        if ((nextHead == null) && (!reachedEndBumper())) {
7274            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7275                    Bundle.getMessage("InfoMessage5",
7276                            new Object[]{block.getUserName()}),
7277                    Bundle.getMessage("MessageTitle"),
7278                    JOptionPane.INFORMATION_MESSAGE);
7279            return;
7280        }
7281        if (!initializeBlockBossLogic(signalHeadName)) {
7282            return;
7283        }
7284        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
7285        logic.setTurnout(turnoutB.getDisplayName());
7286        logic.setSensor1(occupancy.getDisplayName());
7287        if (nextHead != null) {
7288            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7289        }
7290        if (auxSignal != null) {
7291            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7292        }
7293        String nearSensorName = setupNearLogix(layoutTurnoutA, true, a33WayHead);
7294        addNearSensorToLogic(nearSensorName);
7295        if (!layoutTurnoutB.isMainlineC()) {
7296            logic.setLimitSpeed2(true);
7297        }
7298        finalizeBlockBossLogic();
7299    }   //set3WayLogicThroatDivergingB
7300
7301    private void set3WayLogicDivergingA() {
7302        TrackSegment track = (TrackSegment) layoutTurnoutA.getConnectA();
7303        if (track == null) {
7304            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7305                    Bundle.getMessage("InfoMessage7"),
7306                    Bundle.getMessage("MessageTitle"),
7307                    JOptionPane.INFORMATION_MESSAGE);
7308            return;
7309        }
7310        LayoutBlock block = track.getLayoutBlock();
7311        if (block == null) {
7312            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7313                    Bundle.getMessage("InfoMessage6"),
7314                    Bundle.getMessage("MessageTitle"),
7315                    JOptionPane.INFORMATION_MESSAGE);
7316            return;
7317        }
7318        Sensor occupancy = block.getOccupancySensor();
7319        if (occupancy == null) {
7320            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7321                    Bundle.getMessage("InfoMessage4",
7322                            new Object[]{block.getUserName()}),
7323                    Bundle.getMessage("MessageTitle"),
7324                    JOptionPane.INFORMATION_MESSAGE);
7325            return;
7326        }
7327        String signalHeadName = b_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7328        if (signalHeadName == null) {
7329            signalHeadName = "";
7330        }
7331        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutA,
7332                signalHeadName, setSignalsAt3WayTurnoutFrame);
7333        if ((nextHead == null) && (!reachedEndBumper())) {
7334            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7335                    Bundle.getMessage("InfoMessage5",
7336                            new Object[]{block.getUserName()}),
7337                    Bundle.getMessage("MessageTitle"),
7338                    JOptionPane.INFORMATION_MESSAGE);
7339            return;
7340        }
7341        if (!initializeBlockBossLogic(signalHeadName)) {
7342            return;
7343        }
7344        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
7345        logic.setTurnout(turnoutA.getDisplayName());
7346        logic.setSensor1(occupancy.getDisplayName());
7347        if (nextHead != null) {
7348            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7349        }
7350        if (auxSignal != null) {
7351            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7352        }
7353        if (!layoutTurnoutA.isMainlineC()) {
7354            logic.setLimitSpeed2(true);
7355        }
7356        finalizeBlockBossLogic();
7357    }   //set3WayLogicDivergingA
7358
7359    private void set3WayLogicContinuing() {
7360        TrackSegment track = (TrackSegment) layoutTurnoutA.getConnectA();
7361        if (track == null) {
7362            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7363                    Bundle.getMessage("InfoMessage7"),
7364                    Bundle.getMessage("MessageTitle"),
7365                    JOptionPane.INFORMATION_MESSAGE);
7366            return;
7367        }
7368        LayoutBlock block = track.getLayoutBlock();
7369        if (block == null) {
7370            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7371                    Bundle.getMessage("InfoMessage6"),
7372                    Bundle.getMessage("MessageTitle"),
7373                    JOptionPane.INFORMATION_MESSAGE);
7374            return;
7375        }
7376        Sensor occupancy = block.getOccupancySensor();
7377        if (occupancy == null) {
7378            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7379                    Bundle.getMessage("InfoMessage4",
7380                            new Object[]{block.getUserName()}),
7381                    Bundle.getMessage("MessageTitle"),
7382                    JOptionPane.INFORMATION_MESSAGE);
7383            return;
7384        }
7385        String signalHeadName = c_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7386        if (signalHeadName == null) {
7387            signalHeadName = "";
7388        }
7389        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutA,
7390                signalHeadName, setSignalsAt3WayTurnoutFrame);
7391        if ((nextHead == null) && (!reachedEndBumper())) {
7392            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7393                    Bundle.getMessage("InfoMessage5",
7394                            new Object[]{block.getUserName()}),
7395                    Bundle.getMessage("MessageTitle"),
7396                    JOptionPane.INFORMATION_MESSAGE);
7397            return;
7398        }
7399        if (!initializeBlockBossLogic(signalHeadName)) {
7400            return;
7401        }
7402        logic.setMode(BlockBossLogic.TRAILINGMAIN);
7403        logic.setTurnout(turnoutB.getDisplayName());
7404        logic.setSensor1(occupancy.getDisplayName());
7405        if (nextHead != null) {
7406            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7407        }
7408        if (auxSignal != null) {
7409            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7410        }
7411        String nearSensorName = setupNearLogix(layoutTurnoutA, true, c3WayHead);
7412        addNearSensorToLogic(nearSensorName);
7413        if (!layoutTurnoutB.isMainlineB()) {
7414            logic.setLimitSpeed2(true);
7415        }
7416        finalizeBlockBossLogic();
7417    }   //set3WayLogicContinuing
7418
7419    private void set3WayLogicDivergingB() {
7420        TrackSegment track = (TrackSegment) layoutTurnoutA.getConnectA();
7421        if (track == null) {
7422            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7423                    Bundle.getMessage("InfoMessage7"),
7424                    Bundle.getMessage("MessageTitle"),
7425                    JOptionPane.INFORMATION_MESSAGE);
7426            return;
7427        }
7428        LayoutBlock block = track.getLayoutBlock();
7429        if (block == null) {
7430            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7431                    Bundle.getMessage("InfoMessage6"),
7432                    Bundle.getMessage("MessageTitle"),
7433                    JOptionPane.INFORMATION_MESSAGE);
7434            return;
7435        }
7436        Sensor occupancy = block.getOccupancySensor();
7437        if (occupancy == null) {
7438            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7439                    Bundle.getMessage("InfoMessage4",
7440                            new Object[]{block.getUserName()}),
7441                    Bundle.getMessage("MessageTitle"),
7442                    JOptionPane.INFORMATION_MESSAGE);
7443            return;
7444        }
7445        String signalHeadName = d_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7446        if (signalHeadName == null) {
7447            signalHeadName = "";
7448        }
7449        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutA,
7450                signalHeadName, setSignalsAt3WayTurnoutFrame);
7451        if ((nextHead == null) && (!reachedEndBumper())) {
7452            JOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7453                    Bundle.getMessage("InfoMessage5",
7454                            new Object[]{block.getUserName()}),
7455                    Bundle.getMessage("MessageTitle"),
7456                    JOptionPane.INFORMATION_MESSAGE);
7457            return;
7458        }
7459        if (!initializeBlockBossLogic(signalHeadName)) {
7460            return;
7461        }
7462        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
7463        logic.setTurnout(turnoutB.getDisplayName());
7464        logic.setSensor1(occupancy.getDisplayName());
7465        if (nextHead != null) {
7466            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7467        }
7468        if (auxSignal != null) {
7469            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7470        }
7471        String nearSensorName = setupNearLogix(layoutTurnoutA, true, d3WayHead);
7472        addNearSensorToLogic(nearSensorName);
7473        if (!layoutTurnoutB.isMainlineC()) {
7474            logic.setLimitSpeed2(true);
7475        }
7476        finalizeBlockBossLogic();
7477    }   //set3WayLogicDivergingB
7478
7479    /*===========================*\
7480    |* setSensorsAtBlockBoundary *|
7481    \*===========================*/
7482    //
7483    //The following is for placement of sensors and signal masts at points around the layout
7484    //
7485    //This section deals with assigning a sensor to a specific boundary point
7486    BeanDetails<Sensor> westBoundSensor;
7487    BeanDetails<Sensor> eastBoundSensor;
7488
7489    private JmriJFrame setSensorsAtBlockBoundaryFrame = null;
7490    private boolean setSensorsAtBlockBoundaryOpenFlag = false;
7491    private boolean setSensorsAtBlockBoundaryFromMenuFlag = false;
7492
7493    private JButton getAnchorSavedSensors = null;
7494    private JButton changeSensorAtBoundaryIcon = null;
7495    private JButton setSensorsAtBlockBoundaryDone = null;
7496    private JButton setSensorsAtBlockBoundaryCancel = null;
7497
7498    private JFrame sensorFrame = null;
7499    private MultiIconEditor sensorIconEditor = null;
7500
7501    JPanel sensorBlockPanel = new JPanel(new FlowLayout());
7502
7503    public void setSensorsAtBlockBoundaryFromMenu(@Nonnull PositionablePoint p,
7504            @Nonnull MultiIconEditor theEditor,
7505            @Nonnull JFrame theFrame) {
7506        boundary = p;
7507        block1IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
7508        if (boundary.getConnect2() == null) {
7509            block2IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
7510        } else {
7511            block2IDComboBox.setSelectedItem(boundary.getConnect2().getLayoutBlock().getBlock());
7512        }
7513        setSensorsAtBlockBoundaryFromMenuFlag = true;
7514        setSensorsAtBlockBoundary(theEditor, theFrame);
7515        setSensorsAtBlockBoundaryFromMenuFlag = false;
7516    }
7517
7518    //TODO: Add to Tools menu?
7519    public void setSensorsAtBlockBoundary(@Nonnull MultiIconEditor theEditor,
7520            @Nonnull JFrame theFrame) {
7521        sensorIconEditor = theEditor;
7522        sensorFrame = theFrame;
7523
7524        //Initialize if needed
7525        if (setSensorsAtBlockBoundaryFrame == null) {
7526            setSensorsAtBlockBoundaryOpenFlag = false;
7527
7528            westBoundSensor = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
7529            eastBoundSensor = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
7530
7531            setSensorsAtBlockBoundaryFrame = new JmriJFrame(Bundle.getMessage("SensorsAtBoundary"), false, true);
7532            oneFrameToRuleThemAll(setSensorsAtBlockBoundaryFrame);
7533            setSensorsAtBlockBoundaryFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
7534//         setSensorsAtBlockBoundaryFrame.addHelpMenu("package.jmri.jmrit.display.SetSensorsAtBoundary", true);
7535            setSensorsAtBlockBoundaryFrame.setLocation(70, 30);
7536            Container theContentPane = setSensorsAtBlockBoundaryFrame.getContentPane();
7537            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
7538
7539            JPanel header = new JPanel();
7540            header.setLayout(new BoxLayout(header, BoxLayout.Y_AXIS));
7541
7542            JPanel panel11 = new JPanel(new FlowLayout());
7543            block1NameLabel = new JLabel(Bundle.getMessage("MakeLabel",
7544                    Bundle.getMessage("BeanNameBlock") + " 1 - "
7545                    + Bundle.getMessage("Name")));
7546            panel11.add(block1NameLabel);
7547
7548            panel11.add(block1IDComboBox);
7549            block1IDComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
7550            header.add(panel11);
7551
7552            JPanel panel12 = new JPanel(new FlowLayout());
7553            block2NameLabel = new JLabel(Bundle.getMessage("MakeLabel",
7554                    Bundle.getMessage("BeanNameBlock") + " 2 - "
7555                    + Bundle.getMessage("Name")));
7556            panel12.add(block2NameLabel);
7557
7558            panel12.add(block2IDComboBox);
7559            block2IDComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
7560            header.add(panel12);
7561
7562            header.add(new JSeparator(JSeparator.HORIZONTAL));
7563            theContentPane.add(header);
7564
7565            JPanel panel2 = new JPanel(new FlowLayout());
7566            JLabel shTitle = new JLabel(Bundle.getMessage("Sensors"));
7567            panel2.add(shTitle);
7568            panel2.add(new JLabel("   "));
7569            panel2.add(getAnchorSavedSensors = new JButton(Bundle.getMessage("GetSaved")));
7570            getAnchorSavedSensors.addActionListener(this::getSavedAnchorSensors);
7571            getAnchorSavedSensors.setToolTipText(Bundle.getMessage("GetSavedHint"));
7572            theContentPane.add(panel2);
7573
7574            sensorBlockPanel.setLayout(new GridLayout(0, 1));
7575            theContentPane.add(sensorBlockPanel);
7576
7577            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
7578
7579            JPanel panel6 = new JPanel(new FlowLayout());
7580            panel6.add(changeSensorAtBoundaryIcon = new JButton(Bundle.getMessage("ChangeSensorIcon")));
7581            changeSensorAtBoundaryIcon.addActionListener((ActionEvent e) -> sensorFrame.setVisible(true));
7582            changeSensorAtBoundaryIcon.setToolTipText(Bundle.getMessage("ChangeSensorIconHint"));
7583            panel6.add(new JLabel("   "));
7584            panel6.add(setSensorsAtBlockBoundaryDone = new JButton(Bundle.getMessage("ButtonDone")));
7585            setSensorsAtBlockBoundaryDone.addActionListener(this::setSensorsAtBlockBoundaryDonePressed);
7586            setSensorsAtBlockBoundaryDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
7587
7588            panel6.add(setSensorsAtBlockBoundaryCancel = new JButton(Bundle.getMessage("ButtonCancel")));
7589            setSensorsAtBlockBoundaryCancel.addActionListener(this::setSensorsAtBlockBoundaryCancelPressed);
7590            setSensorsAtBlockBoundaryCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
7591            theContentPane.add(panel6, BorderLayout.SOUTH);
7592
7593//make this button the default button (return or enter activates)
7594            JRootPane rootPane = SwingUtilities.getRootPane(setSensorsAtBlockBoundaryDone);
7595            if (rootPane != null) {
7596                rootPane.setDefaultButton(setSensorsAtBlockBoundaryDone);
7597            }
7598
7599            setSensorsAtBlockBoundaryFrame.addWindowListener(new WindowAdapter() {
7600                @Override
7601                public void windowClosing(WindowEvent e) {
7602                    setSensorsAtBlockBoundaryCancelPressed(null);
7603                }
7604            });
7605        }
7606
7607        sensorBlockPanel.removeAll();
7608
7609        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {
7610            eastBoundSensor.setBoundaryTitle(Bundle.getMessage("East/SouthBound"));
7611            if ((setSensorsAtBlockBoundaryFromMenuFlag) && (boundary.getType() == PositionablePoint.PointType.ANCHOR)) {
7612                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7613                    eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
7614                } else {
7615                    eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7616                }
7617            }
7618            eastBoundSensor.getDetailsPanel().setBackground(new Color(255, 255, 200));
7619            sensorBlockPanel.add(eastBoundSensor.getDetailsPanel());
7620
7621            westBoundSensor.setBoundaryTitle(Bundle.getMessage("West/NorthBound"));
7622            if (setSensorsAtBlockBoundaryFromMenuFlag) {
7623                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7624                    westBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7625                } else {
7626                    westBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
7627                }
7628            }
7629            westBoundSensor.getDetailsPanel().setBackground(new Color(200, 255, 255));
7630            sensorBlockPanel.add(westBoundSensor.getDetailsPanel());
7631        } else {
7632            if (setSensorsAtBlockBoundaryFromMenuFlag) {
7633                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7634                    eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7635                    eastBoundSensor.getDetailsPanel().setBackground(new Color(200, 255, 255));
7636                    sensorBlockPanel.add(eastBoundSensor.getDetailsPanel());
7637                } else {
7638                    westBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7639                    westBoundSensor.getDetailsPanel().setBackground(new Color(255, 255, 200));
7640                    sensorBlockPanel.add(westBoundSensor.getDetailsPanel());
7641                }
7642            }
7643        }
7644
7645        block1IDComboBox.setVisible(!setSensorsAtBlockBoundaryFromMenuFlag);
7646        block2IDComboBox.setVisible(!setSensorsAtBlockBoundaryFromMenuFlag);
7647
7648        if (setSensorsAtBlockBoundaryFromMenuFlag) {
7649            block1NameLabel.setText(Bundle.getMessage("MakeLabel",
7650                    Bundle.getMessage("BeanNameBlock") + " 1 "
7651                    + Bundle.getMessage("Name"))
7652                    + " " + boundary.getConnect1().getLayoutBlock().getId());
7653            if (boundary.getConnect2() != null) {
7654                block2NameLabel.setText(Bundle.getMessage("MakeLabel",
7655                        Bundle.getMessage("BeanNameBlock") + " 2 "
7656                        + Bundle.getMessage("Name"))
7657                        + " " + boundary.getConnect2().getLayoutBlock().getId());
7658            }
7659            getSavedAnchorSensors(null);
7660        } else {
7661            block1NameLabel.setText(Bundle.getMessage("MakeLabel",
7662                    Bundle.getMessage("Name") + " 1 "
7663                    + Bundle.getMessage("Name")));
7664            block2NameLabel.setText(Bundle.getMessage("MakeLabel",
7665                    Bundle.getMessage("Name") + " 2  "
7666                    + Bundle.getMessage("Name")));
7667        }
7668        //boundary should never be null... however, just in case...
7669        boolean enable = ((boundary != null) && (boundary.getType() != PositionablePoint.PointType.END_BUMPER));
7670        block2NameLabel.setVisible(enable);
7671
7672        if (!setSensorsAtBlockBoundaryOpenFlag) {
7673            setSensorsAtBlockBoundaryFrame.setPreferredSize(null);
7674            setSensorsAtBlockBoundaryFrame.pack();
7675            setSensorsAtBlockBoundaryOpenFlag = true;
7676        }
7677        setSensorsAtBlockBoundaryFrame.setVisible(true);
7678    }   //setSensorsAtBlockBoundary
7679
7680    /**
7681     * Returns the Sensor corresponding to an entry field in the specified
7682     * dialog.
7683     * <p>
7684     * This also takes care of UpperCase and trimming of leading and
7685     * trailing blanks.
7686     * If entry is required, and no entry is present, an error message is sent.
7687     * An error message also results if a sensor head with the
7688     * entered name is not found in the SensorTable.
7689     * @param sensorName sensor name.
7690     * @param requireEntry true if mandatory field, else false.
7691     * @param frame the main frame.
7692     * @return sensor, may be null.
7693     */
7694    @CheckReturnValue
7695    public Sensor getSensorFromEntry(@CheckForNull String sensorName,
7696            boolean requireEntry,
7697            @Nonnull JmriJFrame frame) {
7698        if ((sensorName == null) || sensorName.isEmpty()) {
7699            if (requireEntry) {
7700                JOptionPane.showMessageDialog(frame, Bundle.getMessage("SensorsError5"),
7701                        Bundle.getMessage("ErrorTitle"),
7702                        JOptionPane.ERROR_MESSAGE);
7703            }
7704            return null;
7705        }
7706        Sensor head = InstanceManager.sensorManagerInstance().getSensor(sensorName);
7707        if (head == null) {
7708            JOptionPane.showMessageDialog(frame,
7709                    Bundle.getMessage("SensorsError4",
7710                            new Object[]{sensorName}), Bundle.getMessage("ErrorTitle"),
7711                    JOptionPane.ERROR_MESSAGE);
7712            return null;
7713        }
7714        return (head);
7715    }
7716
7717    @CheckReturnValue
7718    public SensorIcon getSensorIcon(@Nonnull String sensorName) {
7719        SensorIcon l = new SensorIcon(new NamedIcon("resources/icons/smallschematics/tracksegments/circuit-error.gif",
7720                "resources/icons/smallschematics/tracksegments/circuit-error.gif"), layoutEditor);
7721        l.setIcon("SensorStateActive", sensorIconEditor.getIcon(0));
7722        l.setIcon("SensorStateInactive", sensorIconEditor.getIcon(1));
7723        l.setIcon("BeanStateInconsistent", sensorIconEditor.getIcon(2));
7724        l.setIcon("BeanStateUnknown", sensorIconEditor.getIcon(3));
7725        l.setSensor(sensorName);
7726        return l;
7727    }
7728
7729    /**
7730     * Returns true if the specified Sensor is assigned to an object on the
7731     * panel, regardless of whether an icon is displayed or not. With sensors we
7732     * NO LONGER (4.11.2) allow the same sensor to be allocated in both
7733     * directions.
7734     *
7735     * @param sensor The sensor to be checked.
7736     * @return true if the sensor is currently assigned someplace.
7737     */
7738    public boolean isSensorAssignedAnywhere(@Nonnull Sensor sensor) {
7739        boolean result = false;
7740
7741        //check positionable points
7742        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
7743            if (po.getEastBoundSensor() == sensor) {
7744                result = true;
7745                break;
7746            }
7747            if (po.getWestBoundSensor() == sensor) {
7748                result = true;
7749                break;
7750            }
7751        }
7752        if (!result) {
7753            //check turnouts and slips
7754            for (LayoutTurnout to : layoutEditor.getLayoutTurnoutsAndSlips()) {
7755                if (whereIsSensorAssigned(sensor, to) != LayoutTurnout.Geometry.NONE) {
7756                    result = true;
7757                    break;
7758                }
7759            }
7760        }
7761        if (!result) {
7762            //check level crossings
7763            for (LevelXing x : layoutEditor.getLevelXings()) {
7764                if ((x.getSensorA() != null) && x.getSensorA() == sensor) {
7765                    result = true;
7766                    break;
7767                }
7768                if ((x.getSensorB() != null) && x.getSensorB() == sensor) {
7769                    result = true;
7770                    break;
7771                }
7772                if ((x.getSensorC() != null) && x.getSensorC() == sensor) {
7773                    result = true;
7774                    break;
7775                }
7776                if ((x.getSensorD() != null) && x.getSensorD() == sensor) {
7777                    result = true;
7778                    break;
7779                }
7780            }
7781        }
7782
7783        return result;
7784    }   //isSensorAssignedAnywhere
7785
7786    private LayoutTurnout.Geometry whereIsSensorAssigned(Sensor sensor, LayoutTurnout lTurnout) {
7787        LayoutTurnout.Geometry result = LayoutTurnout.Geometry.NONE;
7788
7789        if (sensor != null && lTurnout != null) {
7790            String sName = sensor.getSystemName();
7791            String uName = sensor.getUserName();
7792
7793            String name = lTurnout.getSensorAName();
7794            if (!name.isEmpty() && name.equals(uName) || name.equals(sName)) {
7795                return LayoutTurnout.Geometry.POINTA1;
7796            }
7797            name = lTurnout.getSensorBName();
7798            if (!name.isEmpty() && name.equals(uName) || name.equals(sName)) {
7799                return LayoutTurnout.Geometry.POINTA2;
7800            }
7801            name = lTurnout.getSensorCName();
7802            if (!name.isEmpty() && name.equals(uName) || name.equals(sName)) {
7803                return LayoutTurnout.Geometry.POINTA3;
7804            }
7805            name = lTurnout.getSensorDName();
7806            if (!name.isEmpty() && name.equals(uName) || name.equals(sName)) {
7807                return LayoutTurnout.Geometry.POINTB1;
7808            }
7809        }
7810        return result;
7811    }
7812
7813    /**
7814     * Display an error dialog.
7815     *
7816     * @param sensor The sensor that is already assigned.
7817     */
7818    void sensorAssignedElseWhere(@Nonnull Sensor sensor) {
7819        JOptionPane.showMessageDialog(setSensorsAtBlockBoundaryFrame,
7820                Bundle.getMessage("SensorsError6", // NOI18N
7821                        new Object[]{sensor.getDisplayName()}),
7822                Bundle.getMessage("ErrorTitle"),
7823                JOptionPane.ERROR_MESSAGE);  // NOI18N
7824    }
7825
7826    /**
7827     * Removes the assignment of the specified Sensor to either a turnout, a
7828     * positionable point, or a level crossing wherever it is assigned. Removes
7829     * any NX Pairs that use the sensor.
7830     * <p>
7831     * If the NX deletes fail due to Conditional references or user deny, the
7832     * assignment is not deleted. No additional notification is necessary since
7833     * they have already been notified or made a choice to not continue.
7834     * <p>
7835     * @param sensor The sensor to be removed.
7836     * @return true if the sensor has been removed.
7837     */
7838    public boolean removeSensorAssignment(@Nonnull Sensor sensor) {
7839        log.trace("Remove sensor assignment at block boundary for '{}'", sensor.getDisplayName());  // NOI18N
7840        if (!InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class).deleteNxPair(sensor)) {
7841            log.trace("Removal of NX pairs for sensor '{}' failed", sensor.getDisplayName());  // NOI18N
7842            return false;
7843        }
7844        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
7845            if (po.getEastBoundSensor() == sensor) {
7846                po.setEastBoundSensor(null);
7847            }
7848            if (po.getWestBoundSensor() == sensor) {
7849                po.setWestBoundSensor(null);
7850            }
7851        }
7852
7853        for (LayoutTurnout to : layoutEditor.getLayoutTurnoutsAndSlips()) {
7854            if (to.getSensorA() == sensor) {
7855                to.setSensorA(null);
7856            }
7857            if (to.getSensorB() == sensor) {
7858                to.setSensorB(null);
7859            }
7860            if (to.getSensorC() == sensor) {
7861                to.setSensorC(null);
7862            }
7863            if (to.getSensorD() == sensor) {
7864                to.setSensorD(null);
7865            }
7866        }
7867
7868        for (LevelXing x : layoutEditor.getLevelXings()) {
7869            if (x.getSensorA() == sensor) {
7870                x.setSensorAName(null);
7871            }
7872            if (x.getSensorB() == sensor) {
7873                x.setSensorBName(null);
7874            }
7875            if (x.getSensorC() == sensor) {
7876                x.setSensorCName(null);
7877            }
7878            if (x.getSensorD() == sensor) {
7879                x.setSensorDName(null);
7880            }
7881        }
7882
7883        return true;
7884    }   //removeSensorAssignment
7885
7886    /**
7887     * Removes the Sensor icon from the panel and from assignment to any
7888     * turnout, positionable point, or level crossing.
7889     *
7890     * @param sensor The sensor whose icon and references are to be removed.
7891     * @return true if the removal was successful.
7892     */
7893    public boolean removeSensorFromPanel(@Nonnull Sensor sensor) {
7894        log.trace("Remove sensor icon and assignment for '{}'", sensor.getDisplayName());  // NOI18N
7895        if (!removeSensorAssignment(sensor)) {
7896            return false;
7897        }
7898
7899        SensorIcon h = null;
7900        int index = -1;
7901        for (int i = 0; (i < layoutEditor.getSensorList().size()) && (index == -1); i++) {
7902            h = layoutEditor.getSensorList().get(i);
7903            if (h.getSensor() == sensor) {
7904                index = i;
7905            }
7906        }
7907        if ((h != null) && (index != -1)) {
7908            layoutEditor.getSensorList().remove(index);
7909            h.remove();
7910            h.dispose();
7911            needRedraw = true;
7912        }
7913        return true;
7914    }
7915
7916    private void getSavedAnchorSensors(ActionEvent a) {
7917        if (!getSimpleBlockInformation()) {
7918            return;
7919        }
7920        eastBoundSensor.setTextField(boundary.getEastBoundSensorName());
7921        westBoundSensor.setTextField(boundary.getWestBoundSensorName());
7922
7923        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {
7924            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7925                eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
7926            } else {
7927                eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7928            }
7929            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7930                westBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7931            } else {
7932                westBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
7933            }
7934        } else {
7935            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7936                westBoundSensor.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7937            } else {
7938                eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7939            }
7940        }
7941
7942        setSensorsAtBlockBoundaryFrame.setPreferredSize(null);
7943        setSensorsAtBlockBoundaryFrame.pack();
7944    }
7945
7946    private void setSensorsAtBlockBoundaryCancelPressed(ActionEvent a) {
7947        setSensorsAtBlockBoundaryOpenFlag = false;
7948        setSensorsAtBlockBoundaryFrame.setVisible(false);
7949    }
7950
7951    private void setSensorsAtBlockBoundaryDonePressed(ActionEvent a) {
7952        log.trace("setSensorsAtBlockBoundaryDonePressed");  // NOI18N
7953        if (!getSimpleBlockInformation()) {
7954            return;
7955        }
7956
7957        Sensor eastSensor = getSensorFromEntry(eastBoundSensor.getText(), false, setSensorsAtBlockBoundaryFrame);
7958        Sensor westSensor = getSensorFromEntry(westBoundSensor.getText(), false, setSensorsAtBlockBoundaryFrame);
7959        Sensor currEastSensor = InstanceManager.sensorManagerInstance().getSensor(boundary.getEastBoundSensorName());
7960        Sensor currWestSensor = InstanceManager.sensorManagerInstance().getSensor(boundary.getWestBoundSensorName());
7961
7962        if (log.isTraceEnabled()) {
7963            log.trace("current sensors: east = {}, west = {}", // NOI18N
7964                    (currEastSensor == null) ? "- none- " : currEastSensor.getDisplayName(), // NOI18N
7965                    (currWestSensor == null) ? "- none- " : currWestSensor.getDisplayName());  // NOI18N
7966            log.trace("new sensors: east = {}, west = {}", // NOI18N
7967                    (eastSensor == null) ? "- none- " : eastSensor.getDisplayName(), // NOI18N
7968                    (westSensor == null) ? "- none- " : westSensor.getDisplayName());  // NOI18N
7969        }
7970
7971        if (eastSensor == null) {
7972            if (currEastSensor != null && removeSensorFromPanel(currEastSensor)) {
7973                boundary.setEastBoundSensor(null);
7974            }
7975        } else if (eastBoundSensor != null) {
7976            setBoundarySensor(eastSensor, currEastSensor, eastBoundSensor, "East");  // NOI18N
7977        }
7978
7979        if (westSensor == null) {
7980            if (currWestSensor != null && removeSensorFromPanel(currWestSensor)) {
7981                boundary.setWestBoundSensor(null);
7982            }
7983        } else if (westBoundSensor != null) {
7984            setBoundarySensor(westSensor, currWestSensor, westBoundSensor, "West");  // NOI18N
7985        }
7986
7987        setSensorsAtBlockBoundaryOpenFlag = false;
7988        setSensorsAtBlockBoundaryFrame.setVisible(false);
7989        if (needRedraw) {
7990            layoutEditor.redrawPanel();
7991            needRedraw = false;
7992            layoutEditor.setDirty();
7993        }
7994    }
7995
7996    /**
7997     * Attached a sensor to the block boundary positional point.
7998     *
7999     * @since 4.11.2
8000     * @param newSensor  The sensor that is being added.
8001     * @param currSensor The sensor that might already be there, otherwise null.
8002     * @param beanDetail The BeanDetails object that contains the supporting
8003     *                   data.
8004     * @param direction  The direction, East or West.
8005     */
8006    void setBoundarySensor(Sensor newSensor, Sensor currSensor,
8007            BeanDetails<Sensor> beanDetail, String direction) {
8008        if (currSensor == null) {
8009            if (!isSensorAssignedAnywhere(newSensor)) {
8010                log.trace("Add sensor '{}'", newSensor.getDisplayName());  // NOI18N
8011                if (direction.equals("West")) {  // NOI18N
8012                    boundary.setWestBoundSensor(beanDetail.getText());
8013                } else {
8014                    boundary.setEastBoundSensor(beanDetail.getText());
8015                }
8016                if (beanDetail.addToPanel()) {
8017                    log.trace("Add icon for sensor '{}'", newSensor.getDisplayName());  // NOI18N
8018                    if (direction.equals("West")) {  // NOI18N
8019                        placeWestBoundIcon(getSensorIcon(beanDetail.getText()),
8020                                beanDetail.isRightSelected(), 0.0);
8021                    } else {
8022                        placeEastBoundIcon(getSensorIcon(beanDetail.getText()),
8023                                beanDetail.isRightSelected(), 0.0);
8024                    }
8025                    needRedraw = true;
8026                }
8027            } else {
8028                sensorAssignedElseWhere(newSensor);
8029            }
8030        } else if (currSensor == newSensor) {
8031            if (beanDetail.addToPanel()) {
8032                if (!isSensorOnPanel(newSensor)) {
8033                    log.trace("Add icon for existing sensor '{}'", newSensor.getDisplayName());  // NOI18N
8034                    if (direction.equals("West")) {  // NOI18N
8035                        placeWestBoundIcon(getSensorIcon(beanDetail.getText()),
8036                                beanDetail.isRightSelected(), 0.0);
8037                    } else {
8038                        placeEastBoundIcon(getSensorIcon(beanDetail.getText()),
8039                                beanDetail.isRightSelected(), 0.0);
8040                    }
8041                    needRedraw = true;
8042                }
8043            }
8044        } else {
8045            if (!isSensorAssignedAnywhere(newSensor)) {
8046                if (removeSensorFromPanel(currSensor)) {
8047                    log.trace("Replace sensor '{}' with sensor '{}'", // NOI18N
8048                            currSensor.getDisplayName(), newSensor.getDisplayName());
8049                    if (direction.equals("West")) {  // NOI18N
8050                        boundary.setWestBoundSensor(beanDetail.getText());
8051                    } else {
8052                        boundary.setEastBoundSensor(beanDetail.getText());
8053                    }
8054                    if (beanDetail.addToPanel()) {
8055                        log.trace("Add icon for replacement sensor '{}'", // NOI18N
8056                                newSensor.getDisplayName());
8057                        if (direction.equals("West")) {  // NOI18N
8058                            placeWestBoundIcon(getSensorIcon(beanDetail.getText()),
8059                                    beanDetail.isRightSelected(), 0.0);
8060                        } else {
8061                            placeEastBoundIcon(getSensorIcon(beanDetail.getText()),
8062                                    beanDetail.isRightSelected(), 0.0);
8063                        }
8064                        needRedraw = true;
8065                    }
8066                }
8067            } else {
8068                sensorAssignedElseWhere(newSensor);
8069            }
8070        }
8071    }
8072
8073    public boolean isSensorOnPanel(@Nonnull Sensor sensor) {
8074        for (SensorIcon s : layoutEditor.getSensorList()) {
8075            if (s.getSensor() == sensor) {
8076                return true;
8077            }
8078        }
8079        return false;
8080    }
8081
8082    /*===============================*\
8083    |* setSignalMastsAtBlockBoundary *|
8084    \*===============================*/
8085    private JmriJFrame setSignalMastsAtBlockBoundaryFrame = null;
8086    private boolean setSignalMastsAtBlockBoundaryOpenFlag = false;
8087    private boolean setSignalMastsAtBlockBoundaryFromMenuFlag = false;
8088
8089    private JButton getAnchorSavedSignalMasts = null;
8090    private JButton setSignalMastsAtBlockBoundaryDone = null;
8091    private JButton setSignalMastsAtBlockBoundaryCancel = null;
8092
8093    BeanDetails<SignalMast> eastSignalMast;
8094    BeanDetails<SignalMast> westSignalMast;
8095
8096    JPanel signalMastBlockPanel = new JPanel(new FlowLayout());
8097
8098    public void setSignalMastsAtBlockBoundaryFromMenu(
8099            @Nonnull PositionablePoint p) {
8100        boundary = p;
8101        block1IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
8102        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {
8103            block2IDComboBox.setSelectedItem(boundary.getConnect2().getLayoutBlock().getBlock());
8104        } else {
8105            block2IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
8106        }
8107        setSignalMastsAtBlockBoundaryFromMenuFlag = true;
8108        setSignalMastsAtBlockBoundary();
8109        setSignalMastsAtBlockBoundaryFromMenuFlag = false;
8110    }
8111
8112    //TODO: Add to Tools menu?
8113    public void setSignalMastsAtBlockBoundary() {
8114
8115        //Initialize if needed
8116        if (setSignalMastsAtBlockBoundaryFrame == null) {
8117            setSignalMastsAtBlockBoundaryOpenFlag = false;
8118
8119            eastSignalMast = new BeanDetails<>("SignalMast", // NOI18N
8120                    InstanceManager.getDefault(SignalMastManager.class));
8121            westSignalMast = new BeanDetails<>("SignalMast", // NOI18N
8122                    InstanceManager.getDefault(SignalMastManager.class));
8123
8124            setSignalMastsAtBlockBoundaryFrame = new JmriJFrame(Bundle.getMessage("SignalMastsAtBoundary"), false, true);
8125            oneFrameToRuleThemAll(setSignalMastsAtBlockBoundaryFrame);
8126            setSignalMastsAtBlockBoundaryFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
8127            //setSignalMastsAtBlockBoundaryFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalMastsAtBoundary", true);
8128            setSignalMastsAtBlockBoundaryFrame.setLocation(70, 30);
8129            Container theContentPane = setSignalMastsAtBlockBoundaryFrame.getContentPane();
8130            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
8131
8132            JPanel header = new JPanel();
8133            header.setLayout(new BoxLayout(header, BoxLayout.Y_AXIS));
8134
8135            //Create the block 1 label and combo box
8136            JPanel panel11 = new JPanel(new FlowLayout());
8137            block1NameLabel = new JLabel(Bundle.getMessage("MakeLabel",
8138                    Bundle.getMessage("BeanNameBlock") + " 1 "
8139                    + Bundle.getMessage("Name")));
8140            panel11.add(block1NameLabel);
8141            panel11.add(block1IDComboBox);
8142            block1IDComboBox.setToolTipText(Bundle.getMessage("SignalMastsBlockNameHint"));
8143            header.add(panel11);
8144
8145            //Create the block 2 label and combo box, visibility will be controlled later
8146            block2NameLabel = new JLabel(Bundle.getMessage("MakeLabel",
8147                    Bundle.getMessage("BeanNameBlock") + " 2 "
8148                    + Bundle.getMessage("Name")));
8149            block2IDComboBox.setToolTipText(Bundle.getMessage("SignalMastsBlockNameHint"));
8150
8151            JPanel panel12 = new JPanel(new FlowLayout());
8152            panel12.add(block2NameLabel);
8153            panel12.add(block2IDComboBox);
8154            header.add(panel12);
8155
8156            header.add(new JSeparator(JSeparator.HORIZONTAL));
8157            theContentPane.add(header);
8158
8159            JPanel panel2 = new JPanel(new FlowLayout());
8160            JLabel shTitle = new JLabel(Bundle.getMessage("SignalMasts"));
8161            panel2.add(shTitle);
8162            panel2.add(new JLabel("   "));
8163            panel2.add(getAnchorSavedSignalMasts = new JButton(Bundle.getMessage("GetSaved")));
8164            getAnchorSavedSignalMasts.addActionListener(this::getSavedAnchorSignalMasts);
8165            getAnchorSavedSignalMasts.setToolTipText(Bundle.getMessage("GetSavedHint"));
8166            theContentPane.add(panel2);
8167
8168            signalMastBlockPanel.setLayout(new GridLayout(0, 1));
8169            theContentPane.add(signalMastBlockPanel);
8170
8171            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
8172
8173            JPanel panel6 = new JPanel(new FlowLayout());
8174            panel6.add(setSignalMastsAtBlockBoundaryDone = new JButton(Bundle.getMessage("ButtonDone")));
8175            setSignalMastsAtBlockBoundaryDone.addActionListener(this::setSignalMastsAtBlockBoundaryDonePressed);
8176            setSignalMastsAtBlockBoundaryDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
8177
8178            panel6.add(setSignalMastsAtBlockBoundaryCancel = new JButton(Bundle.getMessage("ButtonCancel")));
8179            setSignalMastsAtBlockBoundaryCancel.addActionListener(this::setSignalMastsAtBlockBoundaryCancelPressed);
8180            setSignalMastsAtBlockBoundaryCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
8181            theContentPane.add(panel6);
8182
8183            //make this button the default button (return or enter activates)
8184            JRootPane rootPane = SwingUtilities.getRootPane(setSignalMastsAtBlockBoundaryDone);
8185            if (rootPane != null) {
8186                rootPane.setDefaultButton(setSignalMastsAtBlockBoundaryDone);
8187            }
8188
8189            setSignalMastsAtBlockBoundaryFrame.addWindowListener(new WindowAdapter() {
8190                @Override
8191                public void windowClosing(WindowEvent e) {
8192                    setSignalMastsAtBlockBoundaryCancelPressed(null);
8193                }
8194            });
8195        }
8196
8197        eastSignalMast.getCombo().setExcludedItems(new HashSet<>());
8198        westSignalMast.getCombo().setExcludedItems(new HashSet<>());
8199        signalMastBlockPanel.removeAll();
8200
8201        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {   //Anchor points and Edge Connectors
8202            eastSignalMast.setBoundaryTitle(Bundle.getMessage("East/SouthBound"));
8203            if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
8204                eastSignalMast.setBoundaryTitle(Bundle.getMessage("West/NorthBound"));
8205            }
8206            if (setSignalMastsAtBlockBoundaryFromMenuFlag) {
8207                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8208                    eastSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
8209                } else {
8210                    eastSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8211                }
8212            }
8213            eastSignalMast.getDetailsPanel().setBackground(new Color(255, 255, 200));
8214            signalMastBlockPanel.add(eastSignalMast.getDetailsPanel());
8215
8216            westSignalMast.setBoundaryTitle(Bundle.getMessage("West/NorthBound"));
8217            if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
8218                westSignalMast.setBoundaryTitle(Bundle.getMessage("East/SouthBound"));
8219            }
8220            if (setSignalMastsAtBlockBoundaryFromMenuFlag) {
8221                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8222                    westSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8223                } else {
8224                    westSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
8225                }
8226            }
8227            westSignalMast.getDetailsPanel().setBackground(new Color(200, 255, 255));
8228            signalMastBlockPanel.add(westSignalMast.getDetailsPanel());
8229        } else {    //End Bumper
8230            if (setSignalMastsAtBlockBoundaryFromMenuFlag) {
8231                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8232                    eastSignalMast.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8233                    eastSignalMast.getDetailsPanel().setBackground(new Color(200, 255, 255));
8234                    signalMastBlockPanel.add(eastSignalMast.getDetailsPanel());
8235                } else {
8236                    westSignalMast.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8237                    westSignalMast.getDetailsPanel().setBackground(new Color(255, 255, 200));
8238                    signalMastBlockPanel.add(westSignalMast.getDetailsPanel());
8239                }
8240            }
8241        }
8242        block1IDComboBox.setVisible(!setSignalMastsAtBlockBoundaryFromMenuFlag);
8243        block2IDComboBox.setVisible(!setSignalMastsAtBlockBoundaryFromMenuFlag);
8244
8245        if (setSignalMastsAtBlockBoundaryFromMenuFlag) {
8246            block1NameLabel.setText(Bundle.getMessage("MakeLabel",
8247                    Bundle.getMessage("BeanNameBlock") + " 1 "
8248                    + Bundle.getMessage("Name"))
8249                    + " " + boundary.getConnect1().getLayoutBlock().getId());
8250            if (boundary.getConnect2() != null) {
8251                block2NameLabel.setText(Bundle.getMessage("MakeLabel",
8252                        Bundle.getMessage("BeanNameBlock") + " 2 "
8253                        + Bundle.getMessage("Name"))
8254                        + " " + boundary.getConnect2().getLayoutBlock().getId());
8255                block2NameLabel.setVisible(true);
8256            } else {
8257                block2NameLabel.setVisible(false);
8258            }
8259            getSavedAnchorSignalMasts(null);
8260        }
8261
8262        if (!setSignalMastsAtBlockBoundaryOpenFlag) {
8263            setSignalMastsAtBlockBoundaryFrame.setPreferredSize(null);
8264            setSignalMastsAtBlockBoundaryFrame.pack();
8265            setSignalMastsAtBlockBoundaryOpenFlag = true;
8266        }
8267        refreshSignalMastAtBoundaryComboBox();
8268        setSignalMastsAtBlockBoundaryFrame.setVisible(true);
8269    }
8270
8271    /**
8272     * Returns the SignalMast corresponding to an entry field in the specified
8273     * dialog. This also takes care of UpperCase and trimming of leading and
8274     * trailing blanks. If entry is required, and no entry is present, and error
8275     * message is sent. An error message also results if a signalMast head with
8276     * the entered name is not found in the SignalMastTable.
8277     * @param signalMastName name of the signal mast.
8278     * @param requireEntry true if a required field, else false.
8279     * @param frame main frame.
8280     * @return signal mast, may be null.
8281     */
8282    @CheckReturnValue
8283    public SignalMast getSignalMastFromEntry(@CheckForNull String signalMastName,
8284            boolean requireEntry,
8285            @Nonnull JmriJFrame frame) {
8286        if ((signalMastName == null) || signalMastName.isEmpty()) {
8287            if (requireEntry) {
8288                JOptionPane.showMessageDialog(frame, Bundle.getMessage("SignalMastsError5"),
8289                        Bundle.getMessage("ErrorTitle"),
8290                        JOptionPane.ERROR_MESSAGE);
8291            }
8292            return null;
8293
8294        }
8295        SignalMast head = InstanceManager.getDefault(SignalMastManager.class
8296        ).getSignalMast(signalMastName);
8297        if (head == null) {
8298            JOptionPane.showMessageDialog(frame,
8299                    Bundle.getMessage("SignalMastsError4",
8300                            new Object[]{signalMastName}), Bundle.getMessage("ErrorTitle"),
8301                    JOptionPane.ERROR_MESSAGE);
8302            return null;
8303        }
8304        return (head);
8305    }
8306
8307    /**
8308     * Returns true if the specified SignalMast is assigned to an object on the
8309     * panel, regardless of whether an icon is displayed or not.
8310     * @param signalMast the signal mast to query.
8311     * @return true if associated with panel, else false.
8312     */
8313    public boolean isSignalMastAssignedAnywhere(@Nonnull SignalMast signalMast) {
8314        boolean result = false;
8315        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
8316            if ((po.getEastBoundSignalMast() != null) && po.getEastBoundSignalMast() == signalMast) {
8317                result = true;
8318                break;
8319            }
8320            if ((po.getWestBoundSignalMast() != null) && po.getWestBoundSignalMast() == signalMast) {
8321                result = true;
8322                break;
8323            }
8324        }
8325
8326        if (!result) {
8327            for (LayoutTurnout to : layoutEditor.getLayoutTurnoutsAndSlips()) {
8328                if ((to.getSignalAMast() != null) && to.getSignalAMast() == signalMast) {
8329                    result = true;
8330                    break;
8331                }
8332                if ((to.getSignalBMast() != null) && to.getSignalBMast() == signalMast) {
8333                    result = true;
8334                    break;
8335                }
8336                if ((to.getSignalCMast() != null) && to.getSignalCMast() == signalMast) {
8337                    result = true;
8338                    break;
8339                }
8340                if ((to.getSignalDMast() != null) && to.getSignalDMast() == signalMast) {
8341                    result = true;
8342                    break;
8343                }
8344            }
8345        }
8346
8347        if (!result) {
8348            for (LevelXing x : layoutEditor.getLevelXings()) {
8349                if ((x.getSignalAMast() != null) && x.getSignalAMast() == signalMast) {
8350                    result = true;
8351                    break;
8352                }
8353                if ((x.getSignalBMast() != null) && x.getSignalBMast() == signalMast) {
8354                    result = true;
8355                    break;
8356                }
8357                if ((x.getSignalCMast() != null) && x.getSignalCMast() == signalMast) {
8358                    result = true;
8359                    break;
8360                }
8361                if ((x.getSignalDMast() != null) && x.getSignalDMast() == signalMast) {
8362                    result = true;
8363                    break;
8364                }
8365            }
8366        }
8367        return result;
8368    }
8369
8370    /**
8371     * Removes the assignment of the specified SignalMast to either a turnout, a
8372     * positionable point, or a level crossing wherever it is assigned.
8373     * @param signalMast the signal mast to remove.
8374     */
8375    public void removeSignalMastAssignment(@CheckForNull SignalMast signalMast) {
8376        if (signalMast == null) {
8377            return;
8378        }
8379
8380        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
8381            if ((po.getEastBoundSignalMast() != null) && po.getEastBoundSignalMast() == signalMast) {
8382                po.setEastBoundSignalMast(null);
8383            }
8384            if ((po.getWestBoundSignalMast() != null) && po.getWestBoundSignalMast() == signalMast) {
8385                po.setWestBoundSignalMast(null);
8386            }
8387        }
8388        for (LayoutTurnout to : layoutEditor.getLayoutTurnoutsAndSlips()) {
8389            if ((to.getSignalAMast() != null) && to.getSignalAMast() == signalMast) {
8390                to.setSignalAMast(null);
8391            }
8392            if ((to.getSignalBMast() != null) && to.getSignalBMast() == signalMast) {
8393                to.setSignalBMast(null);
8394            }
8395            if ((to.getSignalCMast() != null) && to.getSignalCMast() == signalMast) {
8396                to.setSignalCMast(null);
8397            }
8398            if ((to.getSignalDMast() != null) && to.getSignalDMast() == signalMast) {
8399                to.setSignalDMast(null);
8400            }
8401        }
8402
8403        for (LevelXing x : layoutEditor.getLevelXings()) {
8404            if ((x.getSignalAMast() != null) && x.getSignalAMast() == signalMast) {
8405                x.setSignalAMast(null);
8406            }
8407
8408            if ((x.getSignalBMast() != null) && x.getSignalBMast() == signalMast) {
8409                x.setSignalBMast(null);
8410            }
8411
8412            if ((x.getSignalCMast() != null) && x.getSignalCMast() == signalMast) {
8413                x.setSignalCMast(null);
8414            }
8415
8416            if ((x.getSignalDMast() != null) && x.getSignalDMast() == signalMast) {
8417                x.setSignalDMast(null);
8418            }
8419        }
8420    }
8421
8422    /**
8423     * Removes the SignalMast with the specified name from the panel and from
8424     * assignment to any turnout, positionable point, or level crossing.
8425     * @param signalMast the signal mast to remove.
8426     */
8427    public void removeSignalMastFromPanel(@Nonnull SignalMast signalMast) {
8428        removeSignalMastAssignment(signalMast);
8429        SignalMastIcon h = null;
8430        int index = -1;
8431        for (int i = 0; (i < layoutEditor.getSignalMastList().size()) && (index == -1); i++) {
8432            h = layoutEditor.getSignalMastList().get(i);
8433            if ((h != null) && (h.getSignalMast() == signalMast)) {
8434                index = i;
8435            }
8436        }
8437        if ((h != null) && (index != -1)) {
8438            layoutEditor.getSignalMastList().remove(index);
8439            h.remove();
8440            h.dispose();
8441            needRedraw = true;
8442        }
8443    }
8444
8445    private void getSavedAnchorSignalMasts(ActionEvent a) {
8446        if (!getSimpleBlockInformation()) {
8447            return;
8448        }
8449
8450        eastSignalMast.setTextField(boundary.getEastBoundSignalMastName());
8451        westSignalMast.setTextField(boundary.getWestBoundSignalMastName());
8452
8453        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {
8454            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8455                eastSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
8456            } else {
8457                eastSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8458            }
8459            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8460                westSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8461            } else {
8462                westSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
8463            }
8464        } else {
8465            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8466                westSignalMast.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8467            } else {
8468                eastSignalMast.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8469            }
8470        }
8471        setSignalMastsAtBlockBoundaryFrame.setPreferredSize(null);
8472        setSignalMastsAtBlockBoundaryFrame.pack();
8473    }
8474
8475    private void setSignalMastsAtBlockBoundaryCancelPressed(ActionEvent a) {
8476        setSignalMastsAtBlockBoundaryOpenFlag = false;
8477        setSignalMastsAtBlockBoundaryFrame.setVisible(false);
8478    }
8479
8480    void refreshSignalMastAtBoundaryComboBox() {
8481        createListUsedSignalMasts();
8482        usedMasts.remove(eastSignalMast.getBean());
8483        usedMasts.remove(westSignalMast.getBean());
8484        eastSignalMast.getCombo().setExcludedItems(usedMasts);
8485        westSignalMast.getCombo().setExcludedItems(usedMasts);
8486    }
8487
8488    private void setSignalMastsAtBlockBoundaryDonePressed(ActionEvent a) {
8489        if (!getSimpleBlockInformation()) {
8490            return;
8491        }
8492
8493        SignalMast oldBlock1SignalMast = boundary.getEastBoundSignalMast();
8494        SignalMast block1BoundSignalMast = getSignalMastFromEntry(eastSignalMast.getText(), false, setSignalMastsAtBlockBoundaryFrame);
8495
8496        if (block1BoundSignalMast == null) {
8497            if (InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled()
8498                    && InstanceManager.getDefault(SignalMastLogicManager.class).isSignalMastUsed(oldBlock1SignalMast)) {
8499                SignallingGuiTools.removeSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock1SignalMast);
8500            }
8501
8502            removeSignalMastFromPanel(boundary.getEastBoundSignalMast());
8503            removeSignalMastAssignment(boundary.getEastBoundSignalMast());
8504            boundary.setEastBoundSignalMast("");
8505        }
8506
8507        SignalMast oldBlock2SignalMast = boundary.getWestBoundSignalMast();
8508        SignalMast block2BoundSignalMast = getSignalMastFromEntry(westSignalMast.getText(), false, setSignalMastsAtBlockBoundaryFrame);
8509
8510        if (block2BoundSignalMast == null) {
8511            if (InstanceManager.getDefault(LayoutBlockManager.class
8512            ).isAdvancedRoutingEnabled() && InstanceManager.getDefault(SignalMastLogicManager.class
8513            ).isSignalMastUsed(oldBlock2SignalMast)) {
8514                SignallingGuiTools.removeSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock2SignalMast);
8515            }
8516
8517            removeSignalMastFromPanel(boundary.getWestBoundSignalMast());
8518            removeSignalMastAssignment(boundary.getWestBoundSignalMast());
8519            boundary.setWestBoundSignalMast("");
8520        }
8521        if (block2BoundSignalMast != null && block1BoundSignalMast != null) {
8522            if (block1BoundSignalMast == block2BoundSignalMast) {
8523                JOptionPane.showMessageDialog(setSignalMastsAtBlockBoundaryFrame,
8524                        Bundle.getMessage("SignalMastsError14"),
8525                        Bundle.getMessage("ErrorTitle"),
8526                        JOptionPane.ERROR_MESSAGE);
8527                return;
8528            }
8529            if (oldBlock1SignalMast == block2BoundSignalMast && oldBlock2SignalMast == block1BoundSignalMast) {
8530                //We are going for a swap!
8531                //Need to remove old items first
8532                removeSignalMastFromPanel(boundary.getWestBoundSignalMast());
8533                removeSignalMastFromPanel(boundary.getEastBoundSignalMast());
8534                removeSignalMastAssignment(block1BoundSignalMast);
8535                removeSignalMastAssignment(block2BoundSignalMast);
8536                //Then place new ones
8537                SignalMastIcon l;
8538                if (eastSignalMast.addToPanel()) {
8539                    l = new SignalMastIcon(layoutEditor);
8540                    l.setSignalMast(eastSignalMast.getText());
8541                    placeEastBoundIcon(l, eastSignalMast.isRightSelected(), 0);
8542                }
8543                if (westSignalMast.addToPanel()) {
8544                    l = new SignalMastIcon(layoutEditor);
8545                    l.setSignalMast(westSignalMast.getText());
8546                    placeWestBoundIcon(l, westSignalMast.isRightSelected(), 0);
8547                }
8548                boundary.setEastBoundSignalMast(eastSignalMast.getText());
8549                boundary.setWestBoundSignalMast(westSignalMast.getText());
8550                //Then sort out the logic
8551
8552                if (InstanceManager.getDefault(LayoutBlockManager.class
8553                ).isAdvancedRoutingEnabled()) {
8554                    SignallingGuiTools.swapSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, block1BoundSignalMast, block2BoundSignalMast);
8555                }
8556                needRedraw = true;
8557            }
8558        }
8559        if (!needRedraw) {
8560            if (block1BoundSignalMast != null) {
8561                if (eastSignalMast.addToPanel()) {
8562                    if (isSignalMastAssignedAnywhere(block1BoundSignalMast)
8563                            && (block1BoundSignalMast != oldBlock1SignalMast)) {
8564                        JOptionPane.showMessageDialog(setSignalMastsAtBlockBoundaryFrame,
8565                                Bundle.getMessage("SignalMastsError6",
8566                                        new Object[]{eastSignalMast.getText()}),
8567                                Bundle.getMessage("ErrorTitle"),
8568                                JOptionPane.ERROR_MESSAGE);
8569                        return;
8570                    } else {
8571                        removeSignalMastFromPanel(boundary.getEastBoundSignalMast());
8572                        SignalMastIcon l = new SignalMastIcon(layoutEditor);
8573                        l.setSignalMast(eastSignalMast.getText());
8574                        placeEastBoundIcon(l, eastSignalMast.isRightSelected(), 0);
8575                        removeSignalMastAssignment(block1BoundSignalMast);
8576                        boundary.setEastBoundSignalMast(eastSignalMast.getText());
8577                        needRedraw = true;
8578                    }
8579                } else if ((block1BoundSignalMast != boundary.getEastBoundSignalMast())
8580                        && (block1BoundSignalMast != boundary.getWestBoundSignalMast())) {
8581                    if (isSignalMastOnPanel(block1BoundSignalMast)) {
8582                        JOptionPane.showMessageDialog(setSignalMastsAtBlockBoundaryFrame,
8583                                Bundle.getMessage("SignalMastsError13",
8584                                        new Object[]{eastSignalMast.getText()}),
8585                                Bundle.getMessage("ErrorTitle"),
8586                                JOptionPane.ERROR_MESSAGE);
8587                        return;
8588                    } else {
8589                        removeSignalMastFromPanel(boundary.getEastBoundSignalMast());
8590                        removeSignalMastAssignment(block1BoundSignalMast);
8591                        boundary.setEastBoundSignalMast(eastSignalMast.getText());
8592                    }
8593                }
8594            }
8595            if (block2BoundSignalMast != null) {
8596                if (westSignalMast.addToPanel()) {
8597                    if (isSignalMastAssignedAnywhere(block2BoundSignalMast)
8598                            && (block2BoundSignalMast != oldBlock2SignalMast)) {
8599                        JOptionPane.showMessageDialog(setSignalMastsAtBlockBoundaryFrame,
8600                                Bundle.getMessage("SignalMastsError6",
8601                                        new Object[]{westSignalMast.getText()}),
8602                                Bundle.getMessage("ErrorTitle"),
8603                                JOptionPane.ERROR_MESSAGE);
8604                        return;
8605                    } else /*(oldBlock2SignalMast!=block2BoundSignalMast)*/ {
8606                        removeSignalMastFromPanel(boundary.getWestBoundSignalMast());
8607                        SignalMastIcon l = new SignalMastIcon(layoutEditor);
8608                        l.setSignalMast(westSignalMast.getText());
8609                        placeWestBoundIcon(l, westSignalMast.isRightSelected(), 0);
8610                        removeSignalMastAssignment(block2BoundSignalMast);
8611                        boundary.setWestBoundSignalMast(westSignalMast.getText());
8612                        needRedraw = true;
8613                    }
8614                } else if ((block2BoundSignalMast != boundary.getEastBoundSignalMast())
8615                        && (block2BoundSignalMast != oldBlock2SignalMast)) {
8616                    if (isSignalMastAssignedAnywhere(block2BoundSignalMast)) {
8617                        //Need to do this better, so that the signalMast can be on panel multiple times but only alocated to one anchor at a time
8618                        JOptionPane.showMessageDialog(setSignalMastsAtBlockBoundaryFrame,
8619                                Bundle.getMessage("SignalMastsError13",
8620                                        new Object[]{westSignalMast.getText()}),
8621                                Bundle.getMessage("ErrorTitle"),
8622                                JOptionPane.ERROR_MESSAGE);
8623                        return;
8624                    } else {
8625                        removeSignalMastFromPanel(boundary.getWestBoundSignalMast());
8626                        removeSignalMastAssignment(block2BoundSignalMast);
8627                        boundary.setWestBoundSignalMast(westSignalMast.getText());
8628
8629                    }
8630                }
8631            }
8632
8633            //If advanced routing is enabled and then this indicates that we are using this for discovering the signalmast logic paths.
8634            if (InstanceManager.getDefault(LayoutBlockManager.class
8635            ).isAdvancedRoutingEnabled()
8636                    && (block1BoundSignalMast != null
8637                    || block2BoundSignalMast != null)) {
8638                if ((oldBlock1SignalMast != null) && (block2BoundSignalMast != null)) {
8639                    updateBoundaryBasedSignalMastLogic(
8640                            oldBlock1SignalMast, oldBlock2SignalMast,
8641                            block1BoundSignalMast, block2BoundSignalMast);
8642
8643                }
8644            }
8645        }
8646        setSignalMastsAtBlockBoundaryOpenFlag = false;
8647        setSignalMastsAtBlockBoundaryFrame.setVisible(false);
8648        if (needRedraw) {
8649            layoutEditor.redrawPanel();
8650            needRedraw = false;
8651            layoutEditor.setDirty();
8652        }
8653    }
8654
8655    public void updateBoundaryBasedSignalMastLogic(
8656            @Nonnull SignalMast oldBlock1SignalMast,
8657            @Nonnull SignalMast oldBlock2SignalMast,
8658            @Nonnull SignalMast block1BoundSignalMast,
8659            @Nonnull SignalMast block2BoundSignalMast) {
8660        SignalMastLogicManager smlm = InstanceManager.getDefault(SignalMastLogicManager.class
8661        );
8662        boolean old1Used = smlm.isSignalMastUsed(oldBlock1SignalMast);
8663        boolean old2Used = smlm.isSignalMastUsed(oldBlock2SignalMast);
8664        //Just check that the old ones are used in logics somewhere.
8665        if (old1Used || old2Used) {
8666            boolean new1Used = smlm.isSignalMastUsed(block1BoundSignalMast);
8667            boolean new2Used = smlm.isSignalMastUsed(block2BoundSignalMast);
8668            if (new1Used || new2Used) {
8669                if ((new1Used) && (block1BoundSignalMast != oldBlock1SignalMast)) {
8670                    SignallingGuiTools.removeAlreadyAssignedSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, block1BoundSignalMast);
8671                }
8672                if ((new2Used) && (block2BoundSignalMast != oldBlock2SignalMast)) {
8673                    SignallingGuiTools.removeAlreadyAssignedSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, block2BoundSignalMast);
8674                }
8675            }
8676            if (block1BoundSignalMast != null) {
8677                if (oldBlock2SignalMast != null && old2Used
8678                        && oldBlock2SignalMast == block1BoundSignalMast) {
8679                    SignallingGuiTools.updateSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock2SignalMast, block1BoundSignalMast);
8680                }
8681
8682                if (oldBlock1SignalMast != null && old1Used
8683                        && oldBlock1SignalMast != block1BoundSignalMast) {
8684
8685                    SignallingGuiTools.updateSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock1SignalMast, block1BoundSignalMast);
8686                }
8687            }
8688            if (block2BoundSignalMast != null) {
8689                if (old1Used && oldBlock1SignalMast == block2BoundSignalMast) {
8690
8691                    SignallingGuiTools.updateSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock1SignalMast, block2BoundSignalMast);
8692                }
8693                if (old2Used && oldBlock2SignalMast != block2BoundSignalMast) {
8694                    SignallingGuiTools.updateSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock2SignalMast, block2BoundSignalMast);
8695                }
8696            }
8697        }
8698    }
8699
8700    public void setIconOnPanel(@Nonnull PositionableIcon l,
8701            int rotation, @Nonnull Point p) {
8702        setIconOnPanel(l, rotation, (int) p.getX(), (int) p.getY());
8703    }
8704
8705    public void setIconOnPanel(@Nonnull PositionableIcon l,
8706            int rotation, int xLoc, int yLoc) {
8707        l.setLocation(xLoc, yLoc);
8708        if (rotation > 0) {
8709            l.rotate(rotation);
8710        }
8711        if (l instanceof SignalMastIcon) {
8712            layoutEditor.putSignalMast((SignalMastIcon) l);
8713        } else if (l instanceof SensorIcon) {
8714            layoutEditor.putSensor((SensorIcon) l);
8715        } else if (l instanceof SignalHeadIcon) {
8716            layoutEditor.putSignal((SignalHeadIcon) l);
8717        }
8718    }
8719
8720    private void placeEastBoundIcon(PositionableIcon icon, boolean isRightSide, double fromPoint) {
8721
8722        Point2D p = layoutEditor.getLayoutTrackView(boundary).getCoordsCenter();
8723
8724        //Track segment is used to determine the alignment, therefore this is opposite to the block that we are protecting
8725        TrackSegment t = boundary.getConnect2();
8726        boolean dir = true;
8727        if (boundary.getType() == PositionablePoint.PointType.END_BUMPER) {
8728            t = boundary.getConnect1();
8729        } else {
8730            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8731                t = boundary.getConnect1();
8732            }
8733        }
8734
8735        Point2D pt2;
8736        if (t.getConnect1() == boundary) {
8737            pt2 = layoutEditor.getCoords(t.getConnect2(), t.getType2());
8738        } else {
8739            pt2 = layoutEditor.getCoords(t.getConnect1(), t.getType1());
8740        }
8741        setIconOnPanel(t, icon, dir, p, pt2, isRightSide, fromPoint);
8742    }
8743
8744    private void placeWestBoundIcon(PositionableIcon icon, boolean isRightSide, double fromPoint) {
8745
8746        Point2D p = layoutEditor.getLayoutTrackView(boundary).getCoordsCenter();
8747
8748        //Track segment is used to determine the alignment, therefore this is opposite to the block that we are protecting
8749        TrackSegment t = boundary.getConnect1();
8750        boolean dir = false;
8751        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {
8752            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8753                t = boundary.getConnect2();
8754            }
8755        }
8756
8757        Point2D pt2;
8758        if (t.getConnect1() == boundary) {
8759            pt2 = layoutEditor.getCoords(t.getConnect2(), t.getType2());
8760        } else {
8761            pt2 = layoutEditor.getCoords(t.getConnect1(), t.getType1());
8762        }
8763        setIconOnPanel(t, icon, dir, p, pt2, isRightSide, fromPoint);
8764
8765    }
8766
8767    private void setIconOnPanel(@Nonnull TrackSegment t,
8768            @Nonnull PositionableIcon l,
8769            boolean isEastBound, @Nonnull Point2D pt1, @Nonnull Point2D pt2,
8770            boolean isRightSide, double fromPoint) {
8771        double pt1x = pt1.getX(), pt1y = pt1.getY();
8772        double pt2x = pt2.getX(), pt2y = pt2.getY();
8773
8774        int triX = (int) Math.round(pt2x - pt1x);
8775        int triY = (int) Math.round(pt2y - pt1y);
8776
8777        if (log.isDebugEnabled()) {
8778            log.debug("X {} Y {}", triX, triY);
8779        }
8780        Point loc;
8781        if (triX == 0 || triX == 360) {
8782            //In a vertical Striaght Line
8783            if (isEastBound) {
8784                log.debug("In a vertical striaghtline facing South");
8785                loc = northToSouth(pt1, l, isRightSide, fromPoint);
8786            } else {
8787                log.debug("In a vertical striaghtline facing North");
8788                loc = southToNorth(pt1, l, isRightSide, fromPoint);
8789            }
8790        } else if (triY == 0 || triY == 360) {
8791            //In a Horizontal Straight Line
8792            if (isEastBound) {
8793                log.debug("In a Horizontal striaghtline facing east");
8794                loc = westToEast(pt1, l, isRightSide, fromPoint);
8795            } else {
8796                log.debug("In a Horizontal striaghtline facing west");
8797                loc = eastToWest(pt1, l, isRightSide, fromPoint);
8798            }
8799        } else {
8800            //Compute arc's chord
8801            double a = pt2x - pt1x;
8802            double o = pt2y - pt1y;
8803            double radius = Math.hypot(a, o);  //chord equates to radius of circle
8804
8805            double pt1xa = pt1x + radius;
8806            double a1 = pt2x - pt1xa;
8807            double o1 = pt2y - pt1y;
8808            double chord = Math.hypot(a1, o1);
8809            double rsq = Math.pow(radius, 2);
8810
8811            double radAngleFromDatum = Math.acos((rsq + rsq - Math.pow(chord, 2)) / (2 * rsq));
8812            if (log.isDebugEnabled()) {
8813                log.debug("radius {} Chord {}", radius, chord);
8814                log.debug("Angle from datum line {}", Math.toDegrees(radAngleFromDatum));
8815            }
8816
8817            int rotateDEG = ((int) Math.toDegrees(radAngleFromDatum));
8818            if (log.isDebugEnabled()) {
8819                double tanx = o / a;
8820                double angletanRAD = Math.atan2(o, a);
8821                log.debug("{} = atan2({}, {}) ({})", Math.toDegrees(angletanRAD), o, a, tanx);
8822            }
8823
8824            int oldHeight = l.maxHeight();
8825            int oldWidth = l.maxWidth();
8826
8827            //pt1 is always our boundary point
8828            //East side
8829            if (pt2x > pt1x) {
8830                //East Sides
8831                if (pt2y > pt1y) {
8832                    //"South East Corner"
8833                    rotateDEG = rotateDEG + 270;  //Correct for SM111, sm101, sm121, SM80
8834                    l.rotate(rotateDEG);
8835                    loc = southEastToNorthWest(pt1, l, oldWidth, oldHeight, rotateDEG, isRightSide, fromPoint);
8836                } else {
8837                    //"North East corner" //correct for sm110, sm70, sm131
8838                    rotateDEG = 270 - rotateDEG;
8839                    l.rotate(rotateDEG);
8840                    loc = northEastToSouthWest(pt1, l, oldWidth, oldHeight, rotateDEG, isRightSide, fromPoint);
8841                }
8842
8843            } else {
8844                //West Side
8845                if (pt2y > pt1y) {
8846                    //South West //WORKING FOR SM141, sm130, SM71
8847                    l.rotate(rotateDEG - 90);
8848                    //South West
8849                    loc = southWestToNorthEast(pt1, l, oldWidth, oldHeight, rotateDEG, isRightSide, fromPoint);
8850                } else {
8851                    //North West //Working FOR SM140, SM81, sm120
8852                    rotateDEG = (180 - rotateDEG) + 90;
8853                    l.rotate(rotateDEG);
8854                    loc = northWestToSouthEast(pt1, l, oldWidth, oldHeight, rotateDEG, isRightSide, fromPoint);
8855                }
8856            }
8857        }
8858        setIconOnPanel(l, 0, loc);
8859    }
8860
8861    Point southToNorth(Point2D p, PositionableIcon l, boolean right, double fromPoint) {
8862        int offsetx = 0;
8863        int offsety = (int) (p.getY() + offSetFromPoint + fromPoint);
8864        if (right) {
8865            offsetx = (int) p.getX() + offSetFromPoint;
8866        } else {
8867            offsetx = (int) p.getX() - offSetFromPoint - l.maxWidth();
8868        }
8869        return new Point(offsetx, offsety);
8870    }
8871
8872    Point northToSouth(Point2D p, PositionableIcon l, boolean right, double fromPoint) {
8873        l.rotate(180);
8874        int offsetx = 0;
8875        int offsety = (int) (p.getY() - (offSetFromPoint + fromPoint) - l.maxHeight());
8876        if (right) {
8877            offsetx = (int) p.getX() - offSetFromPoint - l.maxWidth();
8878        } else {
8879            offsetx = (int) p.getX() + offSetFromPoint;
8880        }
8881        return new Point(offsetx, offsety);
8882    }
8883
8884    Point westToEast(Point2D p, PositionableIcon l, boolean right, double fromPoint) {
8885        l.rotate(90);
8886        int offsetx = (int) (p.getX() - (l.maxWidth() + (offSetFromPoint + fromPoint - 1)));
8887        int offsety = 0;
8888        if (right) {
8889            offsety = (int) p.getY() + (offSetFromPoint - 1);
8890        } else {
8891            offsety = (int) p.getY() - (offSetFromPoint) - l.maxHeight();
8892        }
8893        return new Point(offsetx, offsety);
8894    }
8895
8896    Point eastToWest(Point2D p, PositionableIcon l, boolean right, double fromPoint) {
8897        l.rotate(-90);
8898        int offsetx = (int) (p.getX() + offSetFromPoint + fromPoint);
8899        int offsety = 0;
8900        if (right) {
8901            offsety = (int) p.getY() - (offSetFromPoint - 1) - l.maxHeight();
8902        } else {
8903            offsety = (int) p.getY() + (offSetFromPoint);
8904        }
8905        return new Point(offsetx, offsety);
8906    }
8907
8908     // Come back to this as its a bit tight to the rail on SM110 need re
8909     // checking
8910    Point northEastToSouthWest(Point2D p, PositionableIcon l, int oldWidth, int oldHeight, double angleDEG, boolean right, double fromPoint) {
8911        angleDEG = angleDEG - 180;
8912        if (angleDEG < 45) {
8913            //Because of the angle things get shifted about.
8914            int tmpWidth = oldWidth;
8915            oldWidth = oldHeight;
8916            oldHeight = tmpWidth;
8917        }
8918        double ang = angleDEG;
8919        double oppAng = 90 - ang;
8920        double angleRAD = Math.toRadians(angleDEG);
8921        double oppAngRAD = Math.toRadians(oppAng);
8922        double iconAdj = Math.sin(angleRAD) * oldHeight;
8923        double iconAdjOpp = Math.sin(oppAngRAD) * oldHeight;
8924        double bpa = Math.sin(angleRAD) * (offSetFromPoint + fromPoint);
8925        double bpo = Math.sin(oppAngRAD) * (offSetFromPoint + fromPoint);
8926        double ta = Math.sin(angleRAD) * offSetFromPoint;
8927        double to = Math.sin(oppAngRAD) * offSetFromPoint;
8928
8929        if (log.isDebugEnabled()) {
8930            log.debug("north east to south west {}", angleDEG);
8931            log.debug("oldWidth {} oldHeight {}", oldWidth, oldHeight);
8932            log.debug("newWidth {} newHeight {}", l.maxWidth(), l.maxHeight());
8933            log.debug("Icon adj: {} opp adj: {}", iconAdj, iconAdjOpp);
8934            log.debug("boundary point opp {}", bpo);
8935            log.debug("boundary point adj {}", bpa);
8936            log.debug("track opp {}", to);
8937            log.debug("track adj {}", ta);
8938        }
8939        int xpos = 0;
8940        int ypos = 0;
8941        if (right) {
8942            //double x_dist_to_Icon = (l.maxWidth()-iconAdj)-(bpa-bpo);
8943            //double y_dist_to_Icon = bpa+bpo+l.maxHeight();
8944
8945            double x_dist_to_Icon = (iconAdjOpp) - (bpa - to);
8946            double y_dist_to_Icon = ta + bpo + l.maxHeight();
8947
8948            log.debug("x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
8949
8950            xpos = (int) (p.getX() - x_dist_to_Icon);
8951            ypos = (int) (p.getY() - y_dist_to_Icon);
8952
8953        } else {
8954            double y_dist_to_Icon = iconAdjOpp + (bpo - ta);
8955            double x_dist_to_Icon = to + bpa;
8956            //double y_dist_to_Icon = (l.maxHeight()-iconAdj)-(ta-bpo);
8957            //double x_dist_to_Icon = bpa+to;
8958            log.debug("x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
8959
8960            xpos = (int) (p.getX() + x_dist_to_Icon);
8961            ypos = (int) (p.getY() - y_dist_to_Icon);
8962
8963        }
8964        if (log.isDebugEnabled()) {
8965            log.debug("xpos {}", xpos);
8966            log.debug("yPos {}", ypos);
8967        }
8968        return new Point(xpos, ypos);
8969
8970    }
8971
8972    Point southWestToNorthEast(Point2D p, PositionableIcon l, int oldWidth, int oldHeight, double angleDEG, boolean right, double fromPoint) {
8973        angleDEG = 180 - angleDEG;
8974
8975        double oppAng = angleDEG;
8976        double angDEG = 90 - oppAng;
8977
8978        //Because of the angle things get shifted about.
8979        if (angDEG < 45) { //was angle
8980            int tmpWidth = oldWidth;
8981            oldWidth = oldHeight;
8982            oldHeight = tmpWidth;
8983        }
8984
8985        double angRAD = Math.toRadians(angDEG);
8986        double oppAngRAD = Math.toRadians(oppAng);
8987        double iconAdj = Math.sin(angRAD) * oldHeight;
8988        double iconAdjOpp = Math.sin(oppAngRAD) * oldHeight;
8989        double bpa = Math.sin(angRAD) * (offSetFromPoint + fromPoint);
8990        double bpo = Math.sin(oppAngRAD) * (offSetFromPoint + fromPoint);
8991        double ta = Math.sin(angRAD) * offSetFromPoint;
8992        double to = Math.sin(oppAngRAD) * offSetFromPoint;
8993
8994        if (log.isDebugEnabled()) {
8995            log.debug("south west to north east {}", angleDEG);
8996            log.debug("oldWidth {} oldHeight {}", oldWidth, oldHeight);
8997            log.debug("newWidth {} newHeight {}", l.maxWidth(), l.maxHeight());
8998            log.debug("Icon adj: {} opp adj: {}", iconAdj, iconAdjOpp);
8999            log.debug("boundary point opp {}", bpo);
9000            log.debug("boundary point adj {}", bpa);
9001            log.debug("track opp {}", to);
9002            log.debug("track adj {}", ta);
9003        }
9004
9005        int xpos;
9006        int ypos;
9007
9008        if (right) {
9009            double x_dist_to_Icon = iconAdj + (bpa - to);
9010            double y_dist_to_Icon = ta + bpo;
9011            log.debug("x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9012
9013            xpos = (int) (p.getX() - x_dist_to_Icon);
9014            log.debug("xpos {}", xpos);
9015            ypos = (int) (p.getY() + y_dist_to_Icon);
9016            log.debug("yPos {}", ypos);
9017        } else {
9018            double x_dist_to_Icon = (bpa + to) + l.maxWidth();
9019            //double y_dist_to_Icon = (iconAdj+(ta-bpo));
9020            double y_dist_to_Icon = (bpo - ta) - (l.maxHeight() - iconAdjOpp);
9021            //double y_dist_to_Icon = (iconAdj+(ta-bpo));
9022            log.debug("x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9023            xpos = (int) (p.getX() - x_dist_to_Icon);
9024            ypos = (int) (p.getY() + y_dist_to_Icon);
9025        }
9026        if (log.isDebugEnabled()) {
9027            log.debug("xpos {}", xpos);
9028            log.debug("yPos {}", ypos);
9029        }
9030        return new Point(xpos, ypos);
9031
9032    }
9033
9034    //Working FOR SM140, SM81, sm120
9035    Point northWestToSouthEast(Point2D p, PositionableIcon l, int oldWidth, int oldHeight, double angleDEG, boolean right, double fromPoint) {
9036        log.debug("angle before {}", angleDEG);
9037        angleDEG = 180 - angleDEG;
9038        angleDEG = 90 - angleDEG;
9039        log.debug("north west to south east {}", angleDEG);
9040        if (angleDEG < 45) {
9041            //Because of the angle things get shifted about.
9042            int tmpWidth = oldWidth;
9043            oldWidth = oldHeight;
9044            oldHeight = tmpWidth;
9045        }
9046        log.debug("oldWidth {} oldHeight {}", oldWidth, oldHeight);
9047        log.debug("newWidth {} newHeight {}", l.maxWidth(), l.maxHeight());
9048        //double ang = angle;
9049        double oppAng = 90 - angleDEG;
9050        double angleRAD = Math.toRadians(angleDEG);
9051        double oppAngRAD = Math.toRadians(oppAng);
9052        double iconAdj = Math.sin(angleRAD) * oldHeight;
9053        double iconAdjOpp = Math.sin(oppAngRAD) * oldHeight;
9054
9055        double bpa = Math.sin(angleRAD) * (offSetFromPoint + fromPoint);  //distance from point
9056        double bpo = Math.sin(oppAngRAD) * (offSetFromPoint + fromPoint);
9057        double ta = Math.sin(angleRAD) * offSetFromPoint; //distance from track
9058        double to = Math.sin(oppAngRAD) * offSetFromPoint;
9059
9060        if (log.isDebugEnabled()) {
9061            log.debug("north west to south east {}", angleDEG);
9062            log.debug("oldWidth {} oldHeight {}", oldWidth, oldHeight);
9063            log.debug("newWidth {} newHeight {}", l.maxWidth(), l.maxHeight());
9064            log.debug("Icon adj: {} opp adj: {}", iconAdj, iconAdjOpp);
9065            log.debug("boundary point opp {}", bpo);
9066            log.debug("boundary point adj {}", bpa);
9067            log.debug("track opp {}", to);
9068            log.debug("track adj {}", ta);
9069        }
9070        int xpos = 0;
9071        int ypos = 0;
9072        if (right) {
9073            //double x_dist_to_Icon = bpa+bpo+l.maxWidth();
9074            //double y_dist_to_Icon = bpa-(l.maxHeight()-iconAdj);
9075            double x_dist_to_Icon = (l.maxWidth() + ta + bpo);
9076            double y_dist_to_Icon = iconAdj + (bpa - to);
9077
9078            log.debug("right x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9079
9080            xpos = (int) (p.getX() - x_dist_to_Icon);
9081            ypos = (int) (p.getY() - y_dist_to_Icon); //was +
9082        } else {
9083            //This still needs to be worked out.
9084            //double y_dist_to_Icon = bpa+bpo+l.maxHeight();
9085            //double x_dist_to_Icon = iconAdj+(bpa-bpo);
9086
9087            double y_dist_to_Icon = l.maxHeight() + bpa + to;//+(l.maxWidth()-iconAdj);
9088            //double y_dist_to_Icon = bpa-(l.maxHeight()-iconAdj);
9089            //double y_dist_to_Icon = ta+bpo+l.maxHeight();
9090            double x_dist_to_Icon = (iconAdjOpp) + (bpo - ta);
9091            //double x_dist_to_Icon = iconAdj+(bpa-to);
9092            log.debug("left x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9093
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 {}", p.getX(), xpos);
9099            log.debug("{} yPos {}", p.getY(), ypos);
9100        }
9101        return new Point(xpos, ypos);
9102    }
9103
9104    double adjust = (5.0 / 90.0);
9105    int awayright = 5;
9106    private final int offSetFromPoint = 5;
9107
9108    //Correct for SM111, sm101, sm121, SM80
9109    Point southEastToNorthWest(Point2D p, PositionableIcon l, int oldWidth, int oldHeight, double angleDEG, boolean right, double fromPoint) {
9110        angleDEG = 360 - angleDEG;
9111
9112        if (angleDEG > 45) {
9113            //Because of the angle things get shifted about.
9114            int tmpWidth = oldWidth;
9115            int tmpHeight = oldHeight;
9116            oldWidth = tmpWidth;
9117            oldHeight = tmpHeight;
9118        }
9119
9120        // double ang = angle;
9121        double oppAng = 90 - angleDEG;
9122        double angleRAD = Math.toRadians(angleDEG);
9123        double oppAngRAD = Math.toRadians(oppAng);
9124        double iconAdj = Math.sin(angleRAD) * oldHeight;
9125        double iconAdjOpp = Math.sin(oppAngRAD) * oldHeight;
9126        double bpa = Math.sin(angleRAD) * (offSetFromPoint + fromPoint);
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        if (log.isDebugEnabled()) {
9131            log.debug("south east to north west {}", angleDEG);
9132            log.debug("oldWidth {} oldHeight {}", oldWidth, oldHeight);
9133            log.debug("newWidth {} newHeight {}", l.maxWidth(), l.maxHeight());
9134            log.debug("Icon adj: {} opp adj: {}", iconAdj, iconAdjOpp);
9135            log.debug("boundary point opp {}", bpo);
9136            log.debug("boundary point adj {}", bpa);
9137            log.debug("track opp {}", to);
9138            log.debug("track adj {}", ta);
9139        }
9140        int xpos = 0;
9141        int ypos = 0;
9142        if (right) {
9143            //double x_dist_to_Icon = bpa+bpo;
9144            //double y_dist_to_Icon = (iconAdj+bpa-bpo);
9145            double x_dist_to_Icon = bpa + to;
9146            double y_dist_to_Icon = (bpo - ta) - (l.maxHeight() - iconAdjOpp);
9147
9148            log.debug("ydist {}", Double.toString((bpo - ta) - (l.maxHeight() - iconAdjOpp)));
9149            log.debug("   and {}",Double.toString(bpo - (iconAdj + ta)));
9150            /*if(angleDeg<45){
9151    y_dist_to_Icon = (bpo-ta)-(l.maxHeight()-iconAdjOpp);
9152    } else {
9153    y_dist_to_Icon = bpo-(iconAdj+ta);
9154    }*/
9155            //double y_dist_to_Icon = (l.maxHeight()-iconAdj)+(bpo-ta);
9156            xpos = (int) (p.getX() + x_dist_to_Icon);
9157            ypos = (int) (p.getY() + y_dist_to_Icon);
9158            log.debug("right x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9159        } else {
9160            //double x_dist_to_Icon = l.maxWidth()-(iconAdj+(bpa-bpo));
9161            //double y_dist_to_Icon = bpa+bpo;
9162
9163            double x_dist_to_Icon = (bpa - to) - (l.maxWidth() - iconAdj);
9164            double y_dist_to_Icon = bpo + ta;
9165
9166            xpos = (int) (p.getX() + x_dist_to_Icon);
9167            ypos = (int) (p.getY() + y_dist_to_Icon);
9168            log.debug("left x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9169        }
9170        if (log.isDebugEnabled()) {
9171            log.debug("{} xpos {}", p.getX(), xpos);
9172            log.debug("{} yPos {}", p.getY(), ypos);
9173        }
9174
9175        return new Point(xpos, ypos);
9176    }
9177
9178    public boolean isSignalMastOnPanel(@Nonnull SignalMast signalMast) {
9179        for (SignalMastIcon s : layoutEditor.getSignalMastList()) {
9180            if (s.getSignalMast() == signalMast) {
9181                return true;
9182            }
9183        }
9184        return false;
9185    }
9186
9187    /*=========================*\
9188    |* setSignalMastsAtTurnout *|
9189    \*=========================*/
9190    private JmriJFrame setSignalMastsAtTurnoutFrame = null;
9191    private boolean setSignalMastsAtTurnoutOpenFlag = false;
9192    private boolean setSignalMastsAtTurnoutFromMenuFlag = false;
9193
9194    private final NamedBeanComboBox<Turnout> signalMastsTurnoutComboBox = new NamedBeanComboBox<>(
9195            InstanceManager.turnoutManagerInstance(), null,
9196            DisplayOptions.DISPLAYNAME);
9197
9198    private JButton setSignalMastsDone;
9199    private JButton getSavedSignalMasts;
9200    private JButton setSignalMastsCancel;
9201    private JLabel turnoutMastNameLabel = null;
9202
9203    BeanDetails<SignalMast> turnoutSignalMastA;
9204    BeanDetails<SignalMast> turnoutSignalMastB;
9205    BeanDetails<SignalMast> turnoutSignalMastC;
9206    BeanDetails<SignalMast> turnoutSignalMastD;
9207
9208    JPanel signalMastTurnoutPanel = new JPanel(new FlowLayout());
9209
9210    private String[] turnoutBlocks = new String[4];
9211
9212    public void setSignalMastsAtTurnoutFromMenu(@Nonnull LayoutTurnout to,
9213            @Nonnull String[] blocks) {
9214        layoutTurnout = to;
9215        turnout = to.getTurnout();
9216        signalMastsTurnoutComboBox.setSelectedItem(turnout);
9217        turnoutBlocks = new String[4];
9218        for (int i = 0; i < blocks.length; i++) {
9219            turnoutBlocks[i] = blocks[i];
9220        }
9221        setSignalMastsAtTurnoutFromMenuFlag = true;
9222        setSignalMastsAtTurnout();
9223        setSignalMastsAtTurnoutFromMenuFlag = false;
9224    }
9225
9226    //TODO: Add to Tools menu?
9227    public void setSignalMastsAtTurnout() {
9228
9229        //Initialize if needed
9230        if (setSignalMastsAtTurnoutFrame == null) {
9231            setSignalMastsAtTurnoutOpenFlag = false;
9232
9233            turnoutSignalMastA = new BeanDetails<>("SignalMast", // NOI18N
9234                    InstanceManager.getDefault(SignalMastManager.class));
9235            turnoutSignalMastB = new BeanDetails<>("SignalMast", // NOI18N
9236                    InstanceManager.getDefault(SignalMastManager.class));
9237            turnoutSignalMastC = new BeanDetails<>("SignalMast", // NOI18N
9238                    InstanceManager.getDefault(SignalMastManager.class));
9239            turnoutSignalMastD = new BeanDetails<>("SignalMast", // NOI18N
9240                    InstanceManager.getDefault(SignalMastManager.class));
9241
9242            turnoutSignalMastA.getDetailsPanel().setBackground(new Color(255, 255, 200));
9243            turnoutSignalMastB.getDetailsPanel().setBackground(new Color(200, 255, 255));
9244            turnoutSignalMastC.getDetailsPanel().setBackground(new Color(200, 200, 255));
9245            turnoutSignalMastD.getDetailsPanel().setBackground(new Color(255, 200, 200));
9246
9247            setSignalMastsAtTurnoutFrame = new JmriJFrame(Bundle.getMessage("SignalMastsAtTurnout"), false, true);
9248            oneFrameToRuleThemAll(setSignalMastsAtTurnoutFrame);
9249            setSignalMastsAtTurnoutFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
9250//         setSignalMastsAtTurnoutFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalMastsAtTurnout", true);
9251            setSignalMastsAtTurnoutFrame.setLocation(70, 30);
9252            Container theContentPane = setSignalMastsAtTurnoutFrame.getContentPane();
9253            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
9254
9255            JPanel panel1 = new JPanel(new FlowLayout());
9256
9257            turnoutMastNameLabel = new JLabel(
9258                    Bundle.getMessage("BeanNameTurnout")
9259                    + " " + Bundle.getMessage("Name"));
9260            panel1.add(turnoutMastNameLabel);
9261            panel1.add(signalMastsTurnoutComboBox);
9262            signalMastsTurnoutComboBox.setToolTipText(Bundle.getMessage("SignalMastsTurnoutNameHint"));
9263
9264            theContentPane.add(panel1);
9265            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
9266
9267            JPanel panel2 = new JPanel(new FlowLayout());
9268            JLabel shTitle = new JLabel(Bundle.getMessage("SignalMasts"));
9269            panel2.add(shTitle);
9270            panel2.add(new JLabel("   "));
9271            panel2.add(getSavedSignalMasts = new JButton(Bundle.getMessage("GetSaved")));
9272            getSavedSignalMasts.addActionListener(this::turnoutSignalMastsGetSaved);
9273            getSavedSignalMasts.setToolTipText(Bundle.getMessage("GetSavedHint"));
9274            theContentPane.add(panel2);
9275
9276            signalMastTurnoutPanel.setLayout(new GridLayout(0, 2));
9277            theContentPane.add(signalMastTurnoutPanel);
9278            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
9279
9280            JPanel panel6 = new JPanel(new FlowLayout());
9281            panel6.add(new JLabel("   "));
9282            panel6.add(setSignalMastsDone = new JButton(Bundle.getMessage("ButtonDone")));
9283            setSignalMastsDone.addActionListener(this::setSignalMastsDonePressed);
9284            setSignalMastsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
9285
9286            panel6.add(setSignalMastsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
9287            setSignalMastsCancel.addActionListener(this::setSignalMastsCancelPressed);
9288            setSignalMastsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
9289            theContentPane.add(panel6);
9290
9291//make this button the default button (return or enter activates)
9292            JRootPane rootPane = SwingUtilities.getRootPane(setSignalMastsDone);
9293            if (rootPane != null) {
9294                rootPane.setDefaultButton(setSignalMastsDone);
9295            }
9296        }
9297
9298        turnoutSignalMastA.getCombo().setExcludedItems(new HashSet<>());
9299        turnoutSignalMastB.getCombo().setExcludedItems(new HashSet<>());
9300        turnoutSignalMastC.getCombo().setExcludedItems(new HashSet<>());
9301        turnoutSignalMastD.getCombo().setExcludedItems(new HashSet<>());
9302        signalMastTurnoutPanel.removeAll();
9303
9304        signalMastsTurnoutComboBox.setVisible(!setSignalMastsAtTurnoutFromMenuFlag);
9305
9306        if (setSignalMastsAtTurnoutFromMenuFlag) {
9307            turnoutMastNameLabel.setText(Bundle.getMessage("MakeLabel",
9308                    Bundle.getMessage("BeanNameTurnout")
9309                    + " " + Bundle.getMessage("Name"))
9310                    + " " + layoutTurnout.getTurnoutName());
9311        }
9312
9313        if (!setSignalMastsAtTurnoutOpenFlag) {
9314            setSignalMastsAtTurnoutFrame.setPreferredSize(null);
9315            setSignalMastsAtTurnoutFrame.pack();
9316            setSignalMastsAtTurnoutOpenFlag = true;
9317        }
9318        refreshSignalMastAtTurnoutComboBox();
9319        setSignalMastsAtTurnoutFrame.setVisible(true);
9320    }   //setSignalMastsAtTurnout
9321
9322    private void turnoutSignalMastsGetSaved(ActionEvent a) {
9323        if (!getTurnoutMastInformation()) {
9324            return;
9325        }
9326        turnoutBlocks = layoutTurnout.getBlockBoundaries();
9327
9328        turnoutSignalMastA.setTextField(layoutTurnout.getSignalAMastName());
9329        turnoutSignalMastB.setTextField(layoutTurnout.getSignalBMastName());
9330        turnoutSignalMastC.setTextField(layoutTurnout.getSignalCMastName());
9331        turnoutSignalMastD.setTextField(layoutTurnout.getSignalDMastName());
9332
9333        turnoutSignalMastA.setBoundaryLabel(turnoutBlocks[0]);
9334        turnoutSignalMastB.setBoundaryLabel(turnoutBlocks[1]);
9335        turnoutSignalMastC.setBoundaryLabel(turnoutBlocks[2]);
9336        turnoutSignalMastD.setBoundaryLabel(turnoutBlocks[3]);
9337
9338        signalMastTurnoutPanel.removeAll();
9339        boolean boundaryFlag = false;
9340        if (turnoutBlocks[0] != null) {
9341            signalMastTurnoutPanel.add(turnoutSignalMastA.getDetailsPanel());
9342            boundaryFlag = true;
9343        }
9344        if (turnoutBlocks[1] != null) {
9345            signalMastTurnoutPanel.add(turnoutSignalMastB.getDetailsPanel());
9346            boundaryFlag = true;
9347        }
9348        if (turnoutBlocks[2] != null) {
9349            signalMastTurnoutPanel.add(turnoutSignalMastC.getDetailsPanel());
9350            boundaryFlag = true;
9351        }
9352        if (turnoutBlocks[3] != null) {
9353            signalMastTurnoutPanel.add(turnoutSignalMastD.getDetailsPanel());
9354            boundaryFlag = true;
9355        }
9356        if (!boundaryFlag) {
9357            JOptionPane.showMessageDialog(null, Bundle.getMessage("SignalsError20"));
9358        }
9359        setSignalMastsAtTurnoutFrame.setPreferredSize(null);
9360        setSignalMastsAtTurnoutFrame.pack();
9361    }   //turnoutSignalMastsGetSaved
9362
9363    private void setSignalMastsDonePressed(ActionEvent a) {
9364        //process turnout name
9365        if (!getTurnoutMastInformation()) {
9366            return;
9367        }
9368
9369        //process signal head names
9370        SignalMast turnoutMast = getSignalMastFromEntry(turnoutSignalMastA.getText(), false, setSignalsAtTurnoutFrame);
9371        SignalMast turnoutMastB = getSignalMastFromEntry(turnoutSignalMastB.getText(), false, setSignalsAtTurnoutFrame);
9372        SignalMast turnoutMastC = getSignalMastFromEntry(turnoutSignalMastC.getText(), false, setSignalsAtTurnoutFrame);
9373        SignalMast turnoutMastD = getSignalMastFromEntry(turnoutSignalMastD.getText(), false, setSignalsAtTurnoutFrame);
9374
9375        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
9376
9377        //place signals as requested
9378        if (turnoutSignalMastA.addToPanel() && (turnoutMast != null)) {
9379            if (isSignalMastOnPanel(turnoutMast)
9380                    && (turnoutMast != layoutTurnout.getSignalAMast())) {
9381                JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9382                        Bundle.getMessage("SignalsError6",
9383                                new Object[]{turnoutSignalMastA.getText()}),
9384                        Bundle.getMessage("ErrorTitle"),
9385                        JOptionPane.ERROR_MESSAGE);
9386                return;
9387            } else {
9388                removeSignalMastFromPanel(layoutTurnout.getSignalAMast());
9389                SignalMastIcon l = new SignalMastIcon(layoutEditor);
9390                l.setSignalMast(turnoutSignalMastA.getText());
9391                placingBlock(l, turnoutSignalMastA.isRightSelected(),
9392                                0.0, layoutTurnout.getConnectA(), layoutTurnoutView.getCoordsA());
9393                removeAssignment(turnoutMast);
9394                layoutTurnout.setSignalAMast(turnoutSignalMastA.getText());
9395                needRedraw = true;
9396            }
9397        } else if (turnoutMast != null) {
9398            LayoutTurnout.Geometry assigned = isMastAssignedHere(turnoutMast, layoutTurnout);
9399            if (assigned == LayoutTurnout.Geometry.NONE) {
9400                if (isSignalMastOnPanel(turnoutMast)
9401                        && isSignalMastAssignedAnywhere(turnoutMast)) {
9402                    JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9403                            Bundle.getMessage("SignalsError8",
9404                                    new Object[]{turnoutSignalMastA.getText()}),
9405                            Bundle.getMessage("ErrorTitle"),
9406                            JOptionPane.ERROR_MESSAGE);
9407                    return;
9408                } else {
9409                    removeSignalMastFromPanel(layoutTurnout.getSignalAMast());
9410                    removeAssignment(turnoutMast);
9411                    layoutTurnout.setSignalAMast(turnoutSignalMastA.getText());
9412                }
9413                //} else if (assigned != A1) {
9414                //need to figure out what to do in this case.
9415            }
9416        } else {
9417            removeSignalMastFromPanel(layoutTurnout.getSignalAMast());
9418            layoutTurnout.setSignalAMast("");
9419        }
9420        if ((turnoutSignalMastB.addToPanel()) && (turnoutMastB != null)) {
9421            if (isSignalMastOnPanel(turnoutMastB)
9422                    && (turnoutMastB != layoutTurnout.getSignalBMast())) {
9423                JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9424                        Bundle.getMessage("SignalsError6",
9425                                new Object[]{turnoutSignalMastB.getText()}),
9426                        Bundle.getMessage("ErrorTitle"),
9427                        JOptionPane.ERROR_MESSAGE);
9428                return;
9429            } else {
9430                removeSignalMastFromPanel(layoutTurnout.getSignalBMast());
9431                SignalMastIcon l = new SignalMastIcon(layoutEditor);
9432                l.setSignalMast(turnoutSignalMastB.getText());
9433                placingBlock(l, turnoutSignalMastB.isRightSelected(),
9434                                0.0, layoutTurnout.getConnectB(), layoutTurnoutView.getCoordsB());
9435                removeAssignment(turnoutMastB);
9436                layoutTurnout.setSignalBMast(turnoutSignalMastB.getText());
9437                needRedraw = true;
9438            }
9439        } else if (turnoutMastB != null) {
9440            LayoutTurnout.Geometry assigned = isMastAssignedHere(turnoutMastB, layoutTurnout);
9441            if (assigned == LayoutTurnout.Geometry.NONE) {
9442                if (isSignalMastOnPanel(turnoutMastB)
9443                        && isSignalMastAssignedAnywhere(turnoutMastB)) {
9444                    JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9445                            Bundle.getMessage("SignalsError8",
9446                                    new Object[]{turnoutSignalMastB.getText()}),
9447                            Bundle.getMessage("ErrorTitle"),
9448                            JOptionPane.ERROR_MESSAGE);
9449                    return;
9450                } else {
9451                    removeSignalMastFromPanel(layoutTurnout.getSignalBMast());
9452                    removeAssignment(turnoutMastB);
9453                    layoutTurnout.setSignalBMast(turnoutSignalMastB.getText());
9454                }
9455                //} else if (assigned != A2) {
9456                //need to figure out what to do in this case.
9457            }
9458        } else {
9459            removeSignalMastFromPanel(layoutTurnout.getSignalBMast());
9460            layoutTurnout.setSignalBMast("");
9461        }
9462        if (turnoutMastC != null) {
9463            if (turnoutSignalMastC.addToPanel()) {
9464                if (isSignalMastOnPanel(turnoutMastC)
9465                        && (turnoutMastC != layoutTurnout.getSignalCMast())) {
9466                    JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9467                            Bundle.getMessage("SignalsError6",
9468                                    new Object[]{turnoutSignalMastC.getText()}),
9469                            Bundle.getMessage("ErrorTitle"),
9470                            JOptionPane.ERROR_MESSAGE);
9471                    return;
9472                } else {
9473                    removeSignalMastFromPanel(layoutTurnout.getSignalCMast());
9474                    SignalMastIcon l = new SignalMastIcon(layoutEditor);
9475                    l.setSignalMast(turnoutSignalMastC.getText());
9476                    placingBlock(l, turnoutSignalMastC.isRightSelected(),
9477                                    0.0, layoutTurnout.getConnectC(), layoutTurnoutView.getCoordsC());
9478                    removeAssignment(turnoutMastC);
9479                    layoutTurnout.setSignalCMast(turnoutSignalMastC.getText());
9480                    needRedraw = true;
9481                }
9482            } else {
9483                LayoutTurnout.Geometry assigned = isMastAssignedHere(turnoutMastC, layoutTurnout);
9484                if (assigned == LayoutTurnout.Geometry.NONE) {
9485                    if (isSignalMastOnPanel(turnoutMastC)
9486                            && isSignalMastAssignedAnywhere(turnoutMastC)) {
9487                        JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9488                                Bundle.getMessage("SignalsError8",
9489                                        new Object[]{turnoutSignalMastC.getText()}),
9490                                Bundle.getMessage("ErrorTitle"),
9491                                JOptionPane.ERROR_MESSAGE);
9492                        return;
9493                    } else {
9494                        removeSignalMastFromPanel(layoutTurnout.getSignalCMast());
9495                        removeAssignment(turnoutMastC);
9496                        layoutTurnout.setSignalCMast(turnoutSignalMastC.getText());
9497                    }
9498                    //} else if (assigned != A3) {
9499                    //need to figure out what to do in this case.
9500                }
9501            }
9502        } else {
9503            removeSignalMastFromPanel(layoutTurnout.getSignalCMast());
9504            layoutTurnout.setSignalCMast("");
9505        }
9506        if (turnoutMastD != null) {
9507            if (turnoutSignalMastD.addToPanel()) {
9508                if (isSignalMastOnPanel(turnoutMastD)
9509                        && (turnoutMastD != layoutTurnout.getSignalDMast())) {
9510                    String signalHeadName = divergingSignalHeadComboBox.getSelectedItemDisplayName();
9511                    if (signalHeadName == null) {
9512                        signalHeadName = "";
9513                    }
9514                    JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9515                            Bundle.getMessage("SignalsError6",
9516                                    new Object[]{signalHeadName}),
9517                            Bundle.getMessage("ErrorTitle"),
9518                            JOptionPane.ERROR_MESSAGE);
9519                    return;
9520                } else {
9521                    removeSignalMastFromPanel(layoutTurnout.getSignalDMast());
9522                    SignalMastIcon l = new SignalMastIcon(layoutEditor);
9523                    l.setSignalMast(turnoutSignalMastD.getText());
9524                    placingBlock(l, turnoutSignalMastD.isRightSelected(),
9525                                    0.0, layoutTurnout.getConnectD(), layoutTurnoutView.getCoordsD());
9526                    removeAssignment(turnoutMastD);
9527                    layoutTurnout.setSignalDMast(turnoutSignalMastD.getText());
9528                    needRedraw = true;
9529                }
9530            } else {
9531                LayoutTurnout.Geometry assigned = isMastAssignedHere(turnoutMastD, layoutTurnout);
9532                if (assigned == LayoutTurnout.Geometry.NONE) {
9533                    if (isSignalMastOnPanel(turnoutMastD)
9534                            && isSignalMastAssignedAnywhere(turnoutMastD)) {
9535                        JOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9536                                Bundle.getMessage("SignalsError8",
9537                                        new Object[]{turnoutSignalMastD.getText()}),
9538                                Bundle.getMessage("ErrorTitle"),
9539                                JOptionPane.ERROR_MESSAGE);
9540                        return;
9541                    } else {
9542                        removeSignalMastFromPanel(layoutTurnout.getSignalDMast());
9543                        removeAssignment(turnoutMastD);
9544                        layoutTurnout.setSignalDMast(turnoutSignalMastD.getText());
9545                    }
9546                    //} else if (assigned != B1) {
9547                    //need to figure out what to do in this case.
9548                }
9549            }
9550        } else {
9551            removeSignalMastFromPanel(layoutTurnout.getSignalDMast());
9552            layoutTurnout.setSignalDMast("");
9553        }
9554
9555        //make sure this layout turnout is not linked to another
9556        layoutTurnout.setLinkType(LayoutTurnout.LinkType.NO_LINK);
9557        layoutTurnout.setLinkedTurnoutName("");
9558        //finish up
9559        setSignalMastsAtTurnoutOpenFlag = false;
9560        setSignalMastsAtTurnoutFrame.setVisible(false);
9561        if (needRedraw) {
9562            layoutEditor.redrawPanel();
9563            needRedraw = false;
9564            layoutEditor.setDirty();
9565        }
9566    }   //setSignalMastsDonePressed
9567
9568
9569    Set<SignalMast> usedMasts = new HashSet<>();
9570
9571    void createListUsedSignalMasts() {
9572        usedMasts = new HashSet<>();
9573        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
9574            //We allow the same sensor to be allocated in both directions.
9575            if (po != boundary) {
9576                if (po.getEastBoundSignalMast() != null) {
9577                    usedMasts.add(po.getEastBoundSignalMast());
9578                }
9579                if (po.getWestBoundSignalMast() != null) {
9580                    usedMasts.add(po.getWestBoundSignalMast());
9581                }
9582            }
9583        }
9584
9585        for (LayoutTurnout to : layoutEditor.getLayoutTurnoutsAndSlips()) {
9586            if (to.getSignalAMast() != null) {
9587                usedMasts.add(to.getSignalAMast());
9588            }
9589            if (to.getSignalBMast() != null) {
9590                usedMasts.add(to.getSignalBMast());
9591            }
9592            if (to.getSignalCMast() != null) {
9593                usedMasts.add(to.getSignalCMast());
9594            }
9595            if (to.getSignalDMast() != null) {
9596                usedMasts.add(to.getSignalDMast());
9597            }
9598        }
9599        for (LevelXing x : layoutEditor.getLevelXings()) {
9600            if (x.getSignalAMast() != null) {
9601                usedMasts.add(x.getSignalAMast());
9602            }
9603            if (x.getSignalBMast() != null) {
9604                usedMasts.add(x.getSignalBMast());
9605            }
9606            if (x.getSignalCMast() != null) {
9607                usedMasts.add(x.getSignalCMast());
9608            }
9609            if (x.getSignalDMast() != null) {
9610                usedMasts.add(x.getSignalDMast());
9611            }
9612        }
9613    }   //createListUsedSignalMasts
9614
9615    void refreshSignalMastAtTurnoutComboBox() {
9616        turnoutSignalMastsGetSaved(null);
9617        createListUsedSignalMasts();
9618
9619        usedMasts.remove(turnoutSignalMastA.getBean());
9620        usedMasts.remove(turnoutSignalMastB.getBean());
9621        usedMasts.remove(turnoutSignalMastC.getBean());
9622        usedMasts.remove(turnoutSignalMastD.getBean());
9623
9624        turnoutSignalMastA.getCombo().setExcludedItems(usedMasts);
9625        turnoutSignalMastB.getCombo().setExcludedItems(usedMasts);
9626        turnoutSignalMastC.getCombo().setExcludedItems(usedMasts);
9627        turnoutSignalMastD.getCombo().setExcludedItems(usedMasts);
9628    }
9629
9630    private LayoutTurnout.Geometry isMastAssignedHere(
9631            @CheckForNull SignalMast mast,
9632            @CheckForNull LayoutTurnout lTurnout) {
9633        if ((mast == null) || (lTurnout == null)) {
9634            return LayoutTurnout.Geometry.NONE;
9635        }
9636        String sysName = mast.getSystemName();
9637        String uName = mast.getUserName();
9638
9639        String name = lTurnout.getSignalAMastName();
9640        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9641            return LayoutTurnout.Geometry.POINTA1;
9642        }
9643        name = lTurnout.getSignalBMastName();
9644        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9645            return LayoutTurnout.Geometry.POINTA2;
9646        }
9647        name = lTurnout.getSignalCMastName();
9648        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9649            return LayoutTurnout.Geometry.POINTA3;
9650        }
9651        name = lTurnout.getSignalDMastName();
9652        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9653            return LayoutTurnout.Geometry.POINTB1;
9654        }
9655        return LayoutTurnout.Geometry.NONE;
9656    }   //isMastAssignedHere
9657
9658    public void removeAssignment(@Nonnull SignalMast mast) {
9659        String sName = mast.getSystemName();
9660        String uName = mast.getUserName();
9661        for (LayoutTurnout to : layoutEditor.getLayoutTurnouts()) {
9662            if ((to.getSignalAMastName().equals(sName) || ((uName != null)
9663                    && (to.getSignalAMastName().equals(uName))))) {
9664                to.setSignalAMast("");
9665            }
9666            if ((to.getSignalBMastName().equals(sName) || ((uName != null)
9667                    && (to.getSignalBMastName().equals(uName))))) {
9668                to.setSignalBMast("");
9669            }
9670            if ((to.getSignalCMastName().equals(sName) || ((uName != null)
9671                    && (to.getSignalCMastName().equals(uName))))) {
9672                to.setSignalCMast("");
9673            }
9674            if ((to.getSignalDMastName().equals(sName) || ((uName != null)
9675                    && (to.getSignalDMastName().equals(uName))))) {
9676                to.setSignalDMast("");
9677            }
9678        }
9679        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
9680            if (po.getEastBoundSignalMastName().equals(sName) || po.getEastBoundSignalMastName().equals(uName)) {
9681                po.setEastBoundSignalMast("");
9682            }
9683            if (po.getWestBoundSignalMastName().equals(sName) || po.getWestBoundSignalMastName().equals(uName)) {
9684                po.setWestBoundSignalMast("");
9685            }
9686        }
9687        for (LevelXing x : layoutEditor.getLevelXings()) {
9688            if ( (x.getSignalAMastName().equals(sName) || ((uName != null)
9689                    && (x.getSignalAMastName().equals(uName))))) {
9690                x.setSignalAMast("");
9691            }
9692            if ( (x.getSignalBMastName().equals(sName) || ((uName != null)
9693                    && (x.getSignalBMastName().equals(uName))))) {
9694                x.setSignalBMast("");
9695            }
9696            if ( (x.getSignalCMastName().equals(sName) || ((uName != null)
9697                    && (x.getSignalCMastName().equals(uName)))))