001package jmri.jmrit.beantable;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004
005import java.awt.*;
006import java.awt.event.ActionEvent;
007import java.util.ArrayList;
008import java.util.List;
009import java.util.Objects;
010import java.util.Vector;
011
012import javax.annotation.CheckForNull;
013import javax.annotation.Nonnull;
014import javax.swing.*;
015import javax.swing.border.Border;
016import javax.swing.border.TitledBorder;
017
018import jmri.*;
019import jmri.implementation.*;
020import jmri.jmrix.acela.*;
021import jmri.jmrix.grapevine.GrapevineSystemConnectionMemo;
022import jmri.jmrix.grapevine.SerialSignalHead;
023import jmri.util.*;
024import jmri.util.swing.*;
025
026/**
027 * Frame for creating / editing Signal Heads.
028 *
029 * Code originally located within SignalHeadTableAction.java
030 *
031 * @author Bob Jacobsen Copyright (C) 2003,2006,2007, 2008, 2009
032 * @author Petr Koud'a Copyright (C) 2007
033 * @author Egbert Broerse Copyright (C) 2016
034 * @author Steve Young Copyright (C) 2023
035 */
036public class SignalHeadAddEditFrame extends JmriJFrame {
037
038    public SignalHeadAddEditFrame(@CheckForNull SignalHead head){
039        super(Bundle.getMessage(head==null ? "TitleAddSignalHead" : "TitleEditSignalHead"), false, true);
040
041        signalHeadBeingEdited = head;
042    }
043
044    private final SignalHead signalHeadBeingEdited;
045
046    private final NamedBeanHandleManager nbhm = InstanceManager.getDefault(NamedBeanHandleManager.class);
047
048    private final JTextField systemNameField = new JTextField(5);
049    private final JTextField userNameField = new JTextField(10);
050
051    private final JLabel systemNameLabel = new JLabel("");
052    private final JLabel userNameLabel = new JLabel("");
053
054    private JPanel dccOptionsPanel = new JPanel();
055    private JPanel dccAppearanceNumberingPanel = new JPanel();
056    private JPanel dccAppearanceCopyPanel = new JPanel();
057
058    private JPanel acelaHeadPanel = new JPanel();
059
060    // we share input fields across boxes so that
061    // entries in one don't disappear when the user switches
062    // to a different type
063    private JPanel centrePanel1 = new JPanel();
064    private JPanel centrePanel2 = new JPanel();
065    private JPanel centrePanel3 = new JPanel();
066    private JPanel centrePanel4 = new JPanel();
067    private JPanel centrePanel5 = new JPanel();
068    private JPanel centrePanel6 = new JPanel();
069    private JPanel centrePanel7 = new JPanel();
070
071    private final FlowLayout defaultFlow = new FlowLayout(FlowLayout.CENTER, 5, 0);
072    private final Border blackline = BorderFactory.createLineBorder(Color.black);
073
074    private final TitledBorder centrePanBorder1 = BorderFactory.createTitledBorder(blackline);
075    private final TitledBorder centrePanBorder2 = BorderFactory.createTitledBorder(blackline);
076    private final TitledBorder centrePanBorder3 = BorderFactory.createTitledBorder(blackline);
077    private final TitledBorder centrePanBorder4 = BorderFactory.createTitledBorder(blackline);
078    private final TitledBorder centrePanBorder5 = BorderFactory.createTitledBorder(blackline);
079    private final TitledBorder centrePanBorder6 = BorderFactory.createTitledBorder(blackline);
080    private final TitledBorder centrePanBorder7 = BorderFactory.createTitledBorder(blackline);
081
082    private BeanSelectCreatePanel<Turnout> turnoutSelect1;
083    private BeanSelectCreatePanel<Turnout> turnoutSelect2;
084    private BeanSelectCreatePanel<Turnout> turnoutSelect3;
085    private BeanSelectCreatePanel<Turnout> turnoutSelect4;
086    private BeanSelectCreatePanel<Turnout> turnoutSelect5;
087    private BeanSelectCreatePanel<Turnout> turnoutSelect6;
088    private BeanSelectCreatePanel<Turnout> turnoutSelect7;
089
090    private final static String TURNOUT_STATE_THROWN = InstanceManager.getDefault(TurnoutManager.class).getThrownText();
091    private final static String TURNOUT_STATE_CLOSED = InstanceManager.getDefault(TurnoutManager.class).getClosedText();
092
093    private final static int[] TURNOUT_STATE_VALUES = new int[]{Turnout.CLOSED, Turnout.THROWN};
094    private final static String[] TURNOUT_STATE_STRINGS = new String[]{TURNOUT_STATE_CLOSED, TURNOUT_STATE_THROWN};
095
096    private final static String[] SIGNAL_STATE_STRINGS = new String[]{
097        Bundle.getMessage("SignalHeadStateDark"),
098        Bundle.getMessage("SignalHeadStateRed"),
099        Bundle.getMessage("SignalHeadStateLunar"),
100        Bundle.getMessage("SignalHeadStateYellow"),
101        Bundle.getMessage("SignalHeadStateGreen")
102    };
103
104    private final static int[] SIGNAL_STATE_VALUES = new int[]{
105        SignalHead.DARK,
106        SignalHead.RED,
107        SignalHead.LUNAR,
108        SignalHead.YELLOW,
109        SignalHead.GREEN
110    };
111
112    private final static String ACELA_ASPECT = Bundle.getMessage("StringAcelaaspect");
113    private final static String SE8C4_ASPECT = Bundle.getMessage("StringSE8c4aspect");
114    private final static String TRIPLE_OUTPUT = Bundle.getMessage("StringTripleOutput");
115    private final static String QUAD_OUTPUT = Bundle.getMessage("StringQuadOutput");
116    private final static String SINGLE_TURNOUT = Bundle.getMessage("StringSingle");
117    private final static String DOUBLE_TURNOUT = Bundle.getMessage("StringDoubleTurnout");
118    private final static String TRIPLE_TURNOUT = Bundle.getMessage("StringTripleTurnout");
119    private final static String VIRTUAL_HEAD = Bundle.getMessage("StringVirtual");
120    private final static String GRAPEVINE = Bundle.getMessage("StringGrapevine");
121    private final static String LSDEC = Bundle.getMessage("StringLsDec");
122    private final static String DCC_SIGNAL_DECODER = Bundle.getMessage("StringDccSigDec");
123    private final static String MERG_SIGNAL_DRIVER = Bundle.getMessage("StringMerg");
124
125    private final static String ACELA_SIG_HEAD_DOUBLE = Bundle.getMessage("StringSignalheadDouble");
126    private final static String ACELA_SIG_HEAD_TRIPLE = Bundle.getMessage("StringSignalheadTriple");
127    // private final static String ACELA_SIG_HEAD_RGB = Bundle.getMessage("StringSignalheadRGB");
128    private final static String ACELA_SIG_HEAD_BIPLOAR = Bundle.getMessage("StringSignalheadBiPolar");
129    private final static String ACELA_SIG_HEAD_WIGWAG = Bundle.getMessage("StringSignalheadWigwag");
130
131    private final static String[] ACELA_SIG_HEAD_TYPES = new String[]{ACELA_SIG_HEAD_DOUBLE, ACELA_SIG_HEAD_TRIPLE,
132        ACELA_SIG_HEAD_BIPLOAR, ACELA_SIG_HEAD_WIGWAG};
133
134    private final static int[] ACELA_SIG_HEAD_TYPE_VALUES = new int[]{AcelaNode.DOUBLE, AcelaNode.TRIPLE,
135        AcelaNode.BPOLAR, AcelaNode.WIGWAG};
136
137    private final static String[] UK_SEMAPHORE_TYPES = new String[]{Bundle.getMessage("HomeSignal"), Bundle.getMessage("DistantSignal")};
138    private final static String[] UK_SIGNAL_ASPECTS =  new String[]{"2", "3", "4"}; // NOI18N
139
140    private final JLabel dccPacketSendCount = new JLabel(Bundle.getMessage("DCCMastPacketSendCount"));
141    private final JSpinner dccPacketSendCountSpinner = new JSpinner();
142
143    private JSpinner[] dccAspectSpinners;
144    private final JCheckBox dccOffSetAddressCheckBox = new JCheckBox(Bundle.getMessage("DccAccessoryAddressOffSet"));
145
146    private JComboBox<String> headTypeBox;
147    private final JLabel headTypeLabel = new JLabel();
148
149    private final JComboBox<String> prefixBox = new JComboBox<>();
150    private final JLabel prefixBoxLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("DCCSystem")));
151
152    private final JLabel stateLabel1 = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("TurnoutState")));
153    private final JLabel stateLabel2 = new JLabel(stateLabel1.getText()); // faster than Bundle?
154    private final JLabel stateLabel3 = new JLabel(stateLabel1.getText());
155    private final JLabel stateLabel4 = new JLabel(stateLabel1.getText());
156    private final JLabel stateLabel5 = new JLabel(stateLabel1.getText());
157    private final JLabel stateLabel6 = new JLabel(stateLabel1.getText());
158    private final JLabel stateLabel7 = new JLabel(stateLabel1.getText());
159
160    private final JComboBox<String> turnoutStateBox1 = new JComboBox<>(TURNOUT_STATE_STRINGS);
161    private final JComboBox<String> turnoutStateBox2 = new JComboBox<>(TURNOUT_STATE_STRINGS);
162    private final JComboBox<String> turnoutStateBox3 = new JComboBox<>(TURNOUT_STATE_STRINGS);
163    private final JComboBox<String> turnoutStateBox4 = new JComboBox<>(TURNOUT_STATE_STRINGS);
164    private final JComboBox<String> turnoutStateBox5 = new JComboBox<>(TURNOUT_STATE_STRINGS);
165    private final JComboBox<String> turnoutStateBox6 = new JComboBox<>(TURNOUT_STATE_STRINGS);
166    private final JComboBox<String> turnoutStateBox7 = new JComboBox<>(TURNOUT_STATE_STRINGS);
167
168    private final JComboBox<String> signalStateBox2 = new JComboBox<>(SIGNAL_STATE_STRINGS);
169    private final JComboBox<String> signalStateBox3 = new JComboBox<>(SIGNAL_STATE_STRINGS);
170
171    private final JComboBox<String> acelaHeadTypeBox = new JComboBox<>(ACELA_SIG_HEAD_TYPES);
172
173    private final JComboBox<String> ukSignalSemaphoreTypeBox = new JComboBox<>(UK_SEMAPHORE_TYPES);
174    private final JComboBox<String> numberUkAspectsBox = new JComboBox<>(UK_SIGNAL_ASPECTS);
175
176    protected SignalHead getSignalHead(){
177        return signalHeadBeingEdited;
178    }
179
180    protected void resetAddressFields(){
181        this.systemNameField.setText("");
182        this.userNameField.setText("");
183    }
184
185    @Override
186    public void initComponents(){
187
188        addHelpMenu("package.jmri.jmrit.beantable.SignalAddEdit", true);
189
190        for (CommandStation station : InstanceManager.getList(CommandStation.class)) {
191            prefixBox.addItem(station.getUserName());
192        }
193
194        initDccAppearancePanel();
195        turnoutSelect1 = new BeanSelectCreatePanel<>(InstanceManager.getDefault(TurnoutManager.class), null);
196        turnoutSelect2 = new BeanSelectCreatePanel<>(InstanceManager.getDefault(TurnoutManager.class), null);
197        turnoutSelect3 = new BeanSelectCreatePanel<>(InstanceManager.getDefault(TurnoutManager.class), null);
198        turnoutSelect4 = new BeanSelectCreatePanel<>(InstanceManager.getDefault(TurnoutManager.class), null);
199        turnoutSelect5 = new BeanSelectCreatePanel<>(InstanceManager.getDefault(TurnoutManager.class), null);
200        turnoutSelect6 = new BeanSelectCreatePanel<>(InstanceManager.getDefault(TurnoutManager.class), null);
201        turnoutSelect7 = new BeanSelectCreatePanel<>(InstanceManager.getDefault(TurnoutManager.class), null);
202
203        getContentPane().setLayout(new BorderLayout());
204        getContentPane().add(getHeaderPanel(), BorderLayout.PAGE_START);
205        getContentPane().add(new JScrollPane(getCentrePanel()), BorderLayout.CENTER);
206        getContentPane().add(getBottomButtonsPanel(), BorderLayout.PAGE_END);
207
208        typeChanged();
209        setHeadToFrame();
210
211        setEscapeKeyClosesWindow(true);
212        pack();
213        setVisible(true);
214    }
215
216    private JPanel getHeaderPanel(){
217
218        JPanel panelHeader = new JPanel();
219        panelHeader.setLayout(new BoxLayout(panelHeader, BoxLayout.Y_AXIS));
220
221        initTypeBox();
222        panelHeader.add(new JSeparator());
223        panelHeader.add(headTypeBox);
224        panelHeader.add(new JSeparator());
225        JPanel labelPanel = new JPanel();
226        labelPanel.setLayout(new GridBagLayout());
227        labelPanel.add(headTypeLabel);
228
229        panelHeader.add(labelPanel);
230        panelHeader.add(new JSeparator());
231
232        JPanel p = new JPanel();
233        p.setLayout(new FlowLayout());
234        p.add(prefixBoxLabel);
235        prefixBoxLabel.setLabelFor(prefixBox);
236        p.add(prefixBox);
237        panelHeader.add(p);
238
239        p = new JPanel();
240        p.setLayout(new FlowLayout());
241        p.add(systemNameLabel);
242        systemNameLabel.setLabelFor(systemNameField);
243        p.add(systemNameField);
244        panelHeader.add(p);
245
246        p = new JPanel();
247        p.setLayout(new FlowLayout());
248        p.add(userNameLabel);
249        userNameLabel.setLabelFor(userNameField);
250        userNameField.setToolTipText(Bundle.getMessage("SignalHeadUserNameTooltip"));
251        p.add(userNameField);
252        panelHeader.add(p);
253
254        return panelHeader;
255    }
256
257    private JPanel getCentrePanel() {
258
259        JPanel panelCentre = new JPanel();
260        panelCentre.setLayout(new BoxLayout(panelCentre, BoxLayout.Y_AXIS));
261
262        dccOptionsPanel = new JPanel();
263        dccOptionsPanel.add(dccOffSetAddressCheckBox);
264        dccOffSetAddressCheckBox.setToolTipText(Bundle.getMessage("DccOffsetTooltip"));
265        dccOptionsPanel.add(dccPacketSendCount);
266        dccPacketSendCountSpinner.setModel(new SpinnerNumberModel(3, 1, 4, 1));
267        dccPacketSendCountSpinner.setToolTipText(Bundle.getMessage("DCCMastPacketSendCountToolTip"));
268        dccOptionsPanel.add(dccPacketSendCountSpinner);
269        panelCentre.add(dccOptionsPanel);
270
271        centrePanel1 = new JPanel();
272        centrePanel1.setLayout(defaultFlow);
273        centrePanel1.add(turnoutSelect1);
274        centrePanel1.add(stateLabel1);
275        centrePanel1.add(turnoutStateBox1);
276        turnoutStateBox1.setToolTipText(Bundle.getMessage("SignalHeadStateTooltip"));
277        centrePanel1.add(numberUkAspectsBox);
278        numberUkAspectsBox.setToolTipText(Bundle.getMessage("SignalHeadMergTooltip"));
279        numberUkAspectsBox.addActionListener(e -> ukAspectChange());
280        centrePanel1.setBorder(centrePanBorder1);
281        panelCentre.add(centrePanel1);
282
283        centrePanel2 = new JPanel();
284        centrePanel2.setLayout(defaultFlow);
285        centrePanel2.add(turnoutSelect2);
286        centrePanel2.add(stateLabel2);
287        centrePanel2.add(turnoutStateBox2);
288        turnoutStateBox2.setToolTipText(Bundle.getMessage("SignalHeadStateTooltip"));
289        centrePanel2.add(signalStateBox2);
290        centrePanel2.add(ukSignalSemaphoreTypeBox);
291        ukSignalSemaphoreTypeBox.setToolTipText(Bundle.getMessage("SignalHeadUseTooltip"));
292        centrePanel2.add(dccAppearanceNumberingPanel);
293        centrePanel2.setBorder(centrePanBorder2);
294        panelCentre.add(centrePanel2);
295        panelCentre.add(dccAppearanceCopyPanel);
296
297        centrePanel3 = new JPanel();
298        centrePanel3.setLayout(defaultFlow);
299        centrePanel3.add(turnoutSelect3);
300        centrePanel3.add(stateLabel3);
301        centrePanel3.add(turnoutStateBox3);
302        turnoutStateBox3.setToolTipText(Bundle.getMessage("SignalHeadStateTooltip"));
303        centrePanel3.add(signalStateBox3);
304        centrePanel3.setBorder(centrePanBorder3);
305        panelCentre.add(centrePanel3);
306
307        centrePanel4 = new JPanel();
308        centrePanel4.setLayout(defaultFlow);
309        centrePanel4.add(turnoutSelect4);
310        centrePanel4.add(stateLabel4);
311        centrePanel4.add(turnoutStateBox4);
312        turnoutStateBox4.setToolTipText(Bundle.getMessage("SignalHeadStateTooltip"));
313        centrePanel4.setBorder(centrePanBorder4);
314        panelCentre.add(centrePanel4);
315
316        centrePanel5 = new JPanel();
317        centrePanel5.setLayout(defaultFlow);
318        centrePanel5.add(turnoutSelect5);
319        centrePanel5.add(stateLabel5);
320        centrePanel5.add(turnoutStateBox5);
321        turnoutStateBox5.setToolTipText(Bundle.getMessage("SignalHeadStateTooltip"));
322        centrePanel5.setBorder(centrePanBorder5);
323        panelCentre.add(centrePanel5);
324
325        centrePanel6 = new JPanel();
326        centrePanel6.setLayout(defaultFlow);
327        centrePanel6.add(turnoutSelect6);
328        centrePanel6.add(stateLabel6);
329        centrePanel6.add(turnoutStateBox6);
330        turnoutStateBox6.setToolTipText(Bundle.getMessage("SignalHeadStateTooltip"));
331        centrePanel6.setBorder(centrePanBorder6);
332        panelCentre.add(centrePanel6);
333
334        centrePanel7 = new JPanel();
335        centrePanel7.setLayout(defaultFlow);
336        centrePanel7.add(turnoutSelect7);
337        centrePanel7.add(stateLabel7);
338        centrePanel7.add(turnoutStateBox7);
339        turnoutStateBox7.setToolTipText(Bundle.getMessage("SignalHeadStateTooltip"));
340        centrePanel7.setBorder(centrePanBorder7);
341        panelCentre.add(centrePanel7);
342
343        acelaHeadPanel = new JPanel();
344        acelaHeadPanel.setLayout(defaultFlow);
345        JLabel aspectTypeLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelAspectType")));
346        acelaHeadPanel.add(aspectTypeLabel);
347        acelaHeadPanel.add(acelaHeadTypeBox);
348        panelCentre.add(acelaHeadPanel);
349
350        return panelCentre;
351    }
352
353    private JPanel getBottomButtonsPanel(){
354        JPanel panelBottom = new JPanel();
355        panelBottom.setLayout(new BoxLayout(panelBottom, BoxLayout.Y_AXIS));
356        // add buttons
357        JPanel p = new JPanel();
358        p.setLayout(new FlowLayout(FlowLayout.TRAILING));
359
360        JButton cancel = new JButton(Bundle.getMessage("ButtonCancel"));
361        p.add(cancel);
362        cancel.addActionListener(e1 -> dispose());
363
364        JButton update = new JButton(Bundle.getMessage(
365            signalHeadBeingEdited == null ? "ButtonCreate" : "ButtonUpdate" ));
366        update.addActionListener(this::updateEditPressed);
367        getRootPane().setDefaultButton(update);
368
369        p.add(update );
370
371        panelBottom.add(p);
372        return panelBottom;
373    }
374
375    public void hideAllPanels() {
376
377        prefixBoxLabel.setVisible(false);
378        prefixBox.setVisible(false);
379
380        systemNameField.setVisible(false);
381        systemNameLabel.setVisible(false);
382
383        userNameLabel.setVisible(false);
384        userNameField.setVisible(false);
385
386        dccOptionsPanel.setVisible(false);
387        centrePanel1.setVisible(false);
388        turnoutSelect1.setVisible(false);
389        stateLabel1.setVisible(false);
390        turnoutStateBox1.setVisible(false);
391        numberUkAspectsBox.setVisible(false);
392
393        centrePanel2.setVisible(false);
394        turnoutSelect2.setVisible(false);
395        stateLabel2.setVisible(false);
396        turnoutStateBox2.setVisible(false);
397        signalStateBox2.setVisible(false);
398        ukSignalSemaphoreTypeBox.setVisible(false);
399        dccAppearanceNumberingPanel.setVisible(false);
400        dccAppearanceCopyPanel.setVisible(false);
401
402        centrePanel3.setVisible(false);
403        turnoutSelect3.setVisible(false);
404        stateLabel3.setVisible(false);
405        turnoutStateBox3.setVisible(false);
406        signalStateBox3.setVisible(false);
407
408        centrePanel4.setVisible(false);
409        turnoutSelect4.setVisible(false);
410        stateLabel4.setVisible(false);
411        turnoutStateBox4.setVisible(false);
412
413        centrePanel5.setVisible(false);
414        turnoutSelect5.setVisible(false);
415        stateLabel5.setVisible(false);
416        turnoutStateBox5.setVisible(false);
417
418        centrePanel6.setVisible(false);
419        turnoutSelect6.setVisible(false);
420        stateLabel6.setVisible(false);
421        turnoutStateBox6.setVisible(false);
422
423        centrePanel7.setVisible(false);
424        turnoutSelect7.setVisible(false);
425        stateLabel7.setVisible(false);
426        turnoutStateBox7.setVisible(false);
427
428        acelaHeadPanel.setVisible(false);
429    }
430
431    private void setHeadToFrame() {
432        headTypeLabel.setVisible(signalHeadBeingEdited != null);
433        headTypeBox.setVisible(signalHeadBeingEdited == null);
434        if ( signalHeadBeingEdited == null ){
435            headTypeBox.setSelectedItem(DOUBLE_TURNOUT); // force GUI status consistent. Default set to Double Head type
436            typeChanged();
437            systemNameField.setToolTipText(Bundle.getMessage("SignalHeadSysNameTooltip"));
438            return;
439        }
440        headTypeBox.setSelectedItem(getNameFromClass(signalHeadBeingEdited.getClass().getName()));
441        headTypeLabel.setText("<html><h3>"+ getNameFromClass(signalHeadBeingEdited.getClass().getName())+"</h3></html>");
442
443        typeChanged();
444        systemNameField.setEditable(false);
445        systemNameField.setText(signalHeadBeingEdited.getSystemName());
446        userNameField.setText(signalHeadBeingEdited.getUserName());
447
448        String type = (String)headTypeBox.getSelectedItem();
449        if ( QUAD_OUTPUT.equals(type)) {
450            var greenTt = ((QuadOutputSignalHead) signalHeadBeingEdited).getGreen();
451            var yellowTt =((QuadOutputSignalHead) signalHeadBeingEdited).getYellow();
452            var redTt = ((QuadOutputSignalHead) signalHeadBeingEdited).getRed();
453            var lunarTt = ((QuadOutputSignalHead) signalHeadBeingEdited).getLunar();
454            if (greenTt !=null) turnoutSelect1.setDefaultNamedBean(greenTt.getBean());
455            if (yellowTt!=null) turnoutSelect2.setDefaultNamedBean(yellowTt.getBean());
456            if (redTt   !=null) turnoutSelect3.setDefaultNamedBean(redTt.getBean());
457            if (lunarTt !=null) turnoutSelect4.setDefaultNamedBean(lunarTt.getBean());
458        } else if (TRIPLE_TURNOUT.equals(type)) {
459            var greenTt = ((TripleTurnoutSignalHead) signalHeadBeingEdited).getGreen();
460            var yellowTt =((TripleTurnoutSignalHead) signalHeadBeingEdited).getYellow();
461            var redTt = ((TripleTurnoutSignalHead) signalHeadBeingEdited).getRed();
462            if (greenTt !=null) turnoutSelect1.setDefaultNamedBean(greenTt.getBean());
463            if (yellowTt!=null) turnoutSelect2.setDefaultNamedBean(yellowTt.getBean());
464            if (redTt   !=null) turnoutSelect3.setDefaultNamedBean(redTt.getBean());
465        } else if ( TRIPLE_OUTPUT.equals(type)) {
466            var greenTt = ((TripleOutputSignalHead) signalHeadBeingEdited).getGreen();
467            var blueTt =((TripleOutputSignalHead) signalHeadBeingEdited).getBlue();
468            var redTt = ((TripleOutputSignalHead) signalHeadBeingEdited).getRed();
469            if (greenTt !=null) turnoutSelect1.setDefaultNamedBean(greenTt.getBean());
470            if (blueTt!=null) turnoutSelect2.setDefaultNamedBean(blueTt.getBean());
471            if (redTt   !=null) turnoutSelect3.setDefaultNamedBean(redTt.getBean());
472        } else if ( DOUBLE_TURNOUT.equals(type)) {
473            var greenTt = ((DoubleTurnoutSignalHead) signalHeadBeingEdited).getGreen();
474            var redTt = ((DoubleTurnoutSignalHead) signalHeadBeingEdited).getRed();
475            if (greenTt !=null) turnoutSelect1.setDefaultNamedBean(greenTt.getBean());
476            if (redTt   !=null) turnoutSelect2.setDefaultNamedBean(redTt.getBean());
477        } else if ( SINGLE_TURNOUT.equals(type)) {
478            var tTt = ((SingleTurnoutSignalHead) signalHeadBeingEdited).getOutput();
479            if (tTt !=null) turnoutSelect1.setDefaultNamedBean(tTt.getBean());
480            setSignalStateInBox(signalStateBox2, ((SingleTurnoutSignalHead) signalHeadBeingEdited).getOnAppearance());
481            setSignalStateInBox(signalStateBox3, ((SingleTurnoutSignalHead) signalHeadBeingEdited).getOffAppearance());
482        } else if (LSDEC.equals(type)) {  // LDT LS-DEC
483            var greenT = ((LsDecSignalHead) signalHeadBeingEdited).getGreen();
484            var yellowT = ((LsDecSignalHead) signalHeadBeingEdited).getYellow();
485            var redT = ((LsDecSignalHead) signalHeadBeingEdited).getRed();
486            var greenTFlash = ((LsDecSignalHead) signalHeadBeingEdited).getFlashGreen();
487            var yellowTFlash = ((LsDecSignalHead) signalHeadBeingEdited).getFlashYellow();
488            var redTFlash = ((LsDecSignalHead) signalHeadBeingEdited).getFlashRed();
489            var darkT = ((LsDecSignalHead) signalHeadBeingEdited).getDark();
490            if (greenT!=null) turnoutSelect1.setDefaultNamedBean(greenT.getBean());
491            setTurnoutStateInBox(turnoutStateBox1, ((LsDecSignalHead) signalHeadBeingEdited).getGreenState(), TURNOUT_STATE_VALUES);
492            if (yellowT!=null) turnoutSelect2.setDefaultNamedBean(yellowT.getBean());
493            setTurnoutStateInBox(turnoutStateBox2, ((LsDecSignalHead) signalHeadBeingEdited).getYellowState(), TURNOUT_STATE_VALUES);
494            if (redT!=null) turnoutSelect3.setDefaultNamedBean(redT.getBean());
495            setTurnoutStateInBox(turnoutStateBox3, ((LsDecSignalHead) signalHeadBeingEdited).getRedState(), TURNOUT_STATE_VALUES);
496            if (greenTFlash!=null) turnoutSelect4.setDefaultNamedBean(greenTFlash.getBean());
497            setTurnoutStateInBox(turnoutStateBox4, ((LsDecSignalHead) signalHeadBeingEdited).getFlashGreenState(), TURNOUT_STATE_VALUES);
498            if (yellowTFlash!=null) turnoutSelect5.setDefaultNamedBean(yellowTFlash.getBean());
499            setTurnoutStateInBox(turnoutStateBox5, ((LsDecSignalHead) signalHeadBeingEdited).getFlashYellowState(), TURNOUT_STATE_VALUES);
500            if (redTFlash!=null) turnoutSelect6.setDefaultNamedBean(redTFlash.getBean());
501            setTurnoutStateInBox(turnoutStateBox6, ((LsDecSignalHead) signalHeadBeingEdited).getFlashRedState(), TURNOUT_STATE_VALUES);
502            if (darkT!=null) turnoutSelect7.setDefaultNamedBean(darkT.getBean());
503            setTurnoutStateInBox(turnoutStateBox7, ((LsDecSignalHead) signalHeadBeingEdited).getDarkState(), TURNOUT_STATE_VALUES);
504        } else if (ACELA_ASPECT.equals(type)) {
505            AcelaNode tNode = AcelaAddress.getNodeFromSystemName(signalHeadBeingEdited.getSystemName(), InstanceManager.getDefault(AcelaSystemConnectionMemo.class));
506            if (tNode == null) {
507                // node does not exist, ignore call
508                log.error("Can't find new Acela Signal with name '{}", signalHeadBeingEdited.getSystemName());
509                return;
510            }
511            int headnumber = Integer.parseInt(signalHeadBeingEdited.getSystemName().substring(2));
512            setSignalheadTypeInBox(acelaHeadTypeBox, tNode.getOutputSignalHeadType(headnumber), ACELA_SIG_HEAD_TYPE_VALUES);
513        } else if (DCC_SIGNAL_DECODER.equals(type)) {
514            for (int i = 0; i < DccSignalHead.getDefaultValidStates().length; i++) {
515                dccAspectSpinners[i].setValue(((DccSignalHead) signalHeadBeingEdited).getOutputForAppearance(signalHeadBeingEdited.getValidStates()[i]));
516            }
517            dccOffSetAddressCheckBox.setSelected(((DccSignalHead) signalHeadBeingEdited).useAddressOffSet());
518            dccPacketSendCountSpinner.setValue(((DccSignalHead) signalHeadBeingEdited).getDccSignalHeadPacketSendCount());
519
520            for (CommandStation cs : InstanceManager.getList(CommandStation.class)) {
521                if ( signalHeadBeingEdited.getSystemName().startsWith(cs.getSystemPrefix())) {
522                    prefixBox.setSelectedItem(cs.getUserName());
523                }
524            }
525        } else if (MERG_SIGNAL_DRIVER.equals(type)) {
526            setUkSignalAspectsFromBox(numberUkAspectsBox, ((MergSD2SignalHead) signalHeadBeingEdited).getAspects());
527
528            if (((MergSD2SignalHead) signalHeadBeingEdited).getHome()) {
529                setUkSignalType(ukSignalSemaphoreTypeBox, Bundle.getMessage("HomeSignal")); // "Home"
530            } else {
531                setUkSignalType(ukSignalSemaphoreTypeBox, Bundle.getMessage("DistantSignal")); //"Distant"
532            }
533
534            var input1 = ((MergSD2SignalHead) signalHeadBeingEdited).getInput1();
535            var input2 = ((MergSD2SignalHead) signalHeadBeingEdited).getInput2();
536            var input3 = ((MergSD2SignalHead) signalHeadBeingEdited).getInput3();
537            if (input1!=null) turnoutSelect3.setDefaultNamedBean(input1.getBean());
538            if (input2!=null) turnoutSelect4.setDefaultNamedBean(input2.getBean());
539            if (input3!=null) turnoutSelect5.setDefaultNamedBean(input3.getBean());
540
541        }
542
543    }
544
545    private String getNameFromClass(@Nonnull String className){
546        switch (className) {
547            case "jmri.implementation.QuadOutputSignalHead":
548                return QUAD_OUTPUT;
549            case "jmri.implementation.TripleTurnoutSignalHead":
550                return TRIPLE_TURNOUT;
551            case "jmri.implementation.TripleOutputSignalHead":
552                return TRIPLE_OUTPUT;
553            case "jmri.implementation.DoubleTurnoutSignalHead":
554                return DOUBLE_TURNOUT;
555            case "jmri.implementation.SingleTurnoutSignalHead":
556                return SINGLE_TURNOUT;
557            case "jmri.implementation.VirtualSignalHead":
558                return VIRTUAL_HEAD;
559            case "jmri.implementation.LsDecSignalHead":  // LDT LS-DEC
560                return LSDEC;
561            case "jmri.implementation.SE8cSignalHead":
562                return SE8C4_ASPECT;
563            case "jmri.jmrix.grapevine.SerialSignalHead":
564                return GRAPEVINE;
565            case "jmri.jmrix.acela.AcelaSignalHead":
566                return ACELA_ASPECT;
567            case "jmri.implementation.DccSignalHead":
568                return DCC_SIGNAL_DECODER;
569            case "jmri.implementation.MergSD2SignalHead":
570                return MERG_SIGNAL_DRIVER;
571            default:
572                throw new IllegalArgumentException("No implementation for " + className);
573        }
574    }
575
576    private void typeChanged() {
577        hideAllPanels();
578
579        if ( signalHeadBeingEdited == null ){
580            systemNameField.setToolTipText(Bundle.getMessage("SignalHeadSysNameTooltip"));
581        } else {
582            systemNameField.setToolTipText(null);
583        }
584
585        systemNameLabel.setText(Bundle.getMessage("LabelSystemName"));
586        systemNameLabel.setVisible(true);
587        systemNameField.setVisible(true);
588        userNameLabel.setText(Bundle.getMessage("LabelUserName"));
589        userNameLabel.setVisible(true);
590        userNameField.setVisible(true);
591
592        String type = (String)headTypeBox.getSelectedItem();
593        if ( QUAD_OUTPUT.equals(type)) {
594            centrePanBorder1.setTitle(Bundle.getMessage("LabelGreenTurnoutNumber"));
595            centrePanel1.setVisible(true);
596            turnoutSelect1.setVisible(true);
597            centrePanBorder2.setTitle(Bundle.getMessage("LabelYellowTurnoutNumber"));
598            centrePanel2.setVisible(true);
599            turnoutSelect2.setVisible(true);
600            centrePanBorder3.setTitle(Bundle.getMessage("LabelRedTurnoutNumber"));
601            centrePanel3.setVisible(true);
602            turnoutSelect3.setVisible(true);
603            centrePanBorder4.setTitle(Bundle.getMessage("LabelLunarTurnoutNumber"));
604            centrePanel4.setVisible(true);
605            turnoutSelect4.setVisible(true);
606        } else if (TRIPLE_TURNOUT.equals(type)) {
607            centrePanBorder1.setTitle(Bundle.getMessage("LabelGreenTurnoutNumber"));
608            centrePanel1.setVisible(true);
609            turnoutSelect1.setVisible(true);
610            centrePanBorder2.setTitle(Bundle.getMessage("LabelYellowTurnoutNumber"));
611            centrePanel2.setVisible(true);
612            turnoutSelect2.setVisible(true);
613            centrePanBorder3.setTitle(Bundle.getMessage("LabelRedTurnoutNumber"));
614            centrePanel3.setVisible(true);
615            turnoutSelect3.setVisible(true);
616        } else if ( TRIPLE_OUTPUT.equals(type)) {
617            centrePanBorder1.setTitle(Bundle.getMessage("LabelGreenTurnoutNumber"));
618            centrePanel1.setVisible(true);
619            turnoutSelect1.setVisible(true);
620            centrePanBorder2.setTitle(Bundle.getMessage("LabelBlueTurnoutNumber"));
621            centrePanel2.setVisible(true);
622            turnoutSelect2.setVisible(true);
623            centrePanBorder3.setTitle(Bundle.getMessage("LabelRedTurnoutNumber"));
624            centrePanel3.setVisible(true);
625            turnoutSelect3.setVisible(true);
626        } else if ( DOUBLE_TURNOUT.equals(type)) {
627            centrePanBorder1.setTitle(Bundle.getMessage("LabelGreenTurnoutNumber"));
628            centrePanel1.setVisible(true);
629            turnoutSelect1.setVisible(true);
630            centrePanBorder2.setTitle(Bundle.getMessage("LabelRedTurnoutNumber"));
631            centrePanel2.setVisible(true);
632            turnoutSelect2.setVisible(true);
633        } else if ( SINGLE_TURNOUT.equals(type)) {
634            centrePanBorder1.setTitle(Bundle.getMessage("LabelTurnoutNumber"));
635            centrePanel1.setVisible(true);
636            turnoutSelect1.setVisible(true);
637            centrePanBorder2.setTitle(Bundle.getMessage("LabelTurnoutThrownAppearance"));
638            centrePanel2.setVisible(true);
639            signalStateBox2.setVisible(true);
640            centrePanBorder3.setTitle(Bundle.getMessage("LabelTurnoutClosedAppearance"));
641            centrePanel3.setVisible(true);
642            signalStateBox3.setVisible(true);
643        } else if (LSDEC.equals(type)) {  // LDT LS-DEC
644            centrePanBorder1.setTitle(Bundle.getMessage("LabelGreenTurnoutNumber"));
645            centrePanel1.setVisible(true);
646            turnoutSelect1.setVisible(true);
647            stateLabel1.setVisible(true);
648            turnoutStateBox1.setVisible(true);
649
650            centrePanBorder2.setTitle(Bundle.getMessage("LabelYellowTurnoutNumber"));
651            centrePanel2.setVisible(true);
652            turnoutSelect2.setVisible(true);
653            stateLabel2.setVisible(true);
654            turnoutStateBox2.setVisible(true);
655
656            centrePanBorder3.setTitle(Bundle.getMessage("LabelRedTurnoutNumber"));
657            centrePanel3.setVisible(true);
658            turnoutSelect3.setVisible(true);
659            stateLabel3.setVisible(true);
660            turnoutStateBox3.setVisible(true);
661
662            centrePanBorder4.setTitle(Bundle.getMessage("LabelFlashGreenTurnoutNumber"));
663            centrePanel4.setVisible(true);
664            turnoutSelect4.setVisible(true);
665            stateLabel4.setVisible(true);
666            turnoutStateBox4.setVisible(true);
667
668            centrePanBorder5.setTitle(Bundle.getMessage("LabelFlashYellowTurnoutNumber"));
669            centrePanel5.setVisible(true);
670            turnoutSelect5.setVisible(true);
671            stateLabel5.setVisible(true);
672            turnoutStateBox5.setVisible(true);
673
674            centrePanBorder6.setTitle(Bundle.getMessage("LabelFlashRedTurnoutNumber"));
675            centrePanel6.setVisible(true);
676            turnoutSelect6.setVisible(true);
677            stateLabel6.setVisible(true);
678            turnoutStateBox6.setVisible(true);
679
680            centrePanBorder7.setTitle(Bundle.getMessage("LabelDarkTurnoutNumber"));
681            centrePanel7.setVisible(true);
682            turnoutSelect7.setVisible(true);
683            stateLabel7.setVisible(true);
684            turnoutStateBox7.setVisible(true);
685        } else if (ACELA_ASPECT.equals(type)) {
686            acelaHeadPanel.setVisible(true);
687            if ( signalHeadBeingEdited == null ) {
688                systemNameLabel.setText(Bundle.getMessage("LabelSignalheadNumber")); // displays ID instead of -number
689            }
690            systemNameField.setToolTipText(Bundle.getMessage("SignalHeadAcelaTooltip"));
691        } else if (DCC_SIGNAL_DECODER.equals(type)) {
692            if ( signalHeadBeingEdited == null ) {
693                systemNameLabel.setText(Bundle.getMessage("LabelSignalheadNumber")); // displays ID instead of -number
694            }
695            dccOptionsPanel.setVisible(true);
696            prefixBox.setVisible(true);
697            prefixBox.setEnabled(signalHeadBeingEdited==null);
698            prefixBoxLabel.setVisible(true);
699            centrePanBorder2.setTitle(Bundle.getMessage("LabelAspectNumbering"));
700            centrePanel2.setVisible(true);
701            dccAppearanceNumberingPanel.setVisible(true);
702            dccAppearanceCopyPanel.setVisible(true);
703        } else if (MERG_SIGNAL_DRIVER.equals(type)) {
704            centrePanBorder1.setTitle(Bundle.getMessage("NumberOfAppearances")); // same as line 1054
705            centrePanel1.setVisible(true);
706            numberUkAspectsBox.setVisible(true);
707
708            centrePanBorder2.setTitle(Bundle.getMessage("UseAs")); // same as line 1090
709            centrePanel2.setVisible(true);
710            turnoutSelect2.setVisible(false);
711            ukSignalSemaphoreTypeBox.setVisible(true);
712
713            centrePanBorder3.setTitle(Bundle.getMessage("InputNum", " 1 "));
714            centrePanel3.setVisible(true);
715            turnoutSelect3.setVisible(true);
716            centrePanBorder4.setTitle(Bundle.getMessage("InputNum", " 2 "));
717            centrePanel4.setVisible(true);
718            turnoutSelect4.setVisible(true);
719
720            centrePanBorder5.setTitle(Bundle.getMessage("InputNum", " 3 "));
721            centrePanel5.setVisible(true);
722            turnoutSelect5.setVisible(true);
723
724            setUkSignalAspectsFromBox(numberUkAspectsBox, 2);
725            ukAspectChange();
726        } else if ( SE8C4_ASPECT.equals(type)) {
727            systemNameField.setVisible(signalHeadBeingEdited != null);
728            systemNameLabel.setVisible(signalHeadBeingEdited != null);
729
730            centrePanBorder1.setTitle(Bundle.getMessage("LabelTurnoutNumber"));
731            centrePanel1.setVisible(true);
732            turnoutSelect1.setVisible(true);
733
734            centrePanBorder2.setTitle(Bundle.getMessage("LabelSecondNumber"));
735            centrePanel2.setVisible(true);
736            turnoutSelect2.setVisible(true);
737        }
738        else if ( GRAPEVINE.equals(type)) {}
739        else if ( VIRTUAL_HEAD.equals(type)) {}
740        else {
741            log.error("Cannot edit SignalHead of unrecognized type: {}", type);
742        }
743        pack();
744    }
745
746    private void initTypeBox() {
747        headTypeBox = new JComboBox<>(new String[]{ACELA_ASPECT, DCC_SIGNAL_DECODER, DOUBLE_TURNOUT, LSDEC, MERG_SIGNAL_DRIVER, QUAD_OUTPUT, SINGLE_TURNOUT, SE8C4_ASPECT, TRIPLE_TURNOUT, TRIPLE_OUTPUT, VIRTUAL_HEAD});
748        // If no DCC Command station is found, remove the DCC Signal Decoder option.
749        if (prefixBox.getItemCount() == 0) {
750            headTypeBox.removeItem(DCC_SIGNAL_DECODER);
751        }
752        if (!InstanceManager.getList(GrapevineSystemConnectionMemo.class).isEmpty()) {
753            headTypeBox.addItem(GRAPEVINE);
754        }
755        if (InstanceManager.getList(AcelaSystemConnectionMemo.class).isEmpty()) {
756            headTypeBox.removeItem(ACELA_ASPECT);
757        }
758        headTypeBox.addActionListener(e1 -> typeChanged());
759        JComboBoxUtil.setupComboBoxMaxRows(headTypeBox);
760        headTypeBox.setEnabled(signalHeadBeingEdited==null);
761    }
762
763    private void initDccAppearancePanel() {
764        dccAppearanceNumberingPanel = new JPanel();
765        dccAppearanceNumberingPanel.setLayout(new GridLayout(0, 2));
766        dccAspectSpinners = new JSpinner[DccSignalHead.getDefaultValidStates().length];
767        for (int i = 0; i < DccSignalHead.getDefaultValidStates().length; i++) {
768            String aspect = DccSignalHead.getDefaultValidStateNames()[i];
769            dccAppearanceNumberingPanel.add(new JLabel(aspect));
770
771            SpinnerNumberModel DccSpinnerModel = new SpinnerNumberModel(1, 0, 31, 1);
772            JSpinner tmp = new JSpinner(DccSpinnerModel);
773            tmp.setValue(DccSignalHead.getDefaultNumberForAppearance(DccSignalHead.getDefaultValidStates()[i]));
774            dccAspectSpinners[i] = tmp; // store the whole JSpinner
775            dccAppearanceNumberingPanel.add(tmp); // and display that copy on the JPanel
776            tmp.setToolTipText(Bundle.getMessage("DccAccessoryAspect", i));
777        }
778
779        // Use appearance numbers from signal head option
780        var copyPanel = new JPanel();
781        copyPanel.add(new JLabel(Bundle.getMessage("LabelCopyAppearanceNumbers")));
782        copyPanel.add(copyFromHeadSelection());
783        dccAppearanceCopyPanel.add(copyPanel);
784    }
785
786    /**
787     * Create a combobox of DCC signal heads that can be used to supply a set of appearance numbers.
788     * This makes it easy to override the default appearance numbers.
789     * @return a combobox with the current DCC signal head display names.
790     */
791    @Nonnull JComboBox<String> copyFromHeadSelection() {
792        List<String> headList = new ArrayList<>();
793        for (SignalHead head : InstanceManager.getDefault(SignalHeadManager.class).getNamedBeanSet()) {
794            if (head instanceof DccSignalHead){
795                headList.add(head.getDisplayName());
796            }
797        }
798
799        headList.sort(null);
800        JComboBox<String> headSelect = new JComboBox<String>(new Vector<String>(headList));
801
802        if (headList.size() == 0) {
803            headSelect.setEnabled(false);
804        } else {
805            headSelect.insertItemAt("", 0);
806            headSelect.setSelectedIndex(0);
807            headSelect.addActionListener((ActionEvent e) -> {
808                @SuppressWarnings("unchecked") // e.getSource() cast from mastSelect source
809                JComboBox<String> eb = (JComboBox<String>) e.getSource();
810                String sourceHead = (String) eb.getSelectedItem();
811                if (sourceHead != null && !sourceHead.isEmpty()) {
812                    copyFromAnotherDCCHeadAppearances(sourceHead);
813                }
814            });
815        }
816        return headSelect;
817    }
818
819    /**
820     * Change the appearance number spinners to the appearance numbers from a different signal head.
821     * @param headName User or system name of head to copy from
822     */
823    void copyFromAnotherDCCHeadAppearances(@Nonnull String headName) {
824        DccSignalHead head = (DccSignalHead) InstanceManager.getDefault(SignalHeadManager.class).getNamedBean(headName);
825        if (head == null) {
826            log.error("can't copy appearance numbers from another signal head because {} doesn't exist", headName);
827            return;
828        }
829
830        var keys = head.getValidStateKeys();
831        for (int i = 0; i < keys.length; i++) {
832            dccAspectSpinners[i].setValue(head.getOutputForAppearance(head.getValidStates()[i]));
833        }
834    }
835
836    private void setSignalStateInBox(JComboBox<String> box, int state) {
837        switch (state) {
838            case SignalHead.DARK:
839                box.setSelectedIndex(0);
840                break;
841            case SignalHead.RED:
842                box.setSelectedIndex(1);
843                break;
844            case SignalHead.LUNAR:
845                box.setSelectedIndex(2);
846                break;
847            case SignalHead.YELLOW:
848                box.setSelectedIndex(3);
849                break;
850            case SignalHead.GREEN:
851                box.setSelectedIndex(4);
852                break;
853            case SignalHead.FLASHRED:
854                box.setSelectedIndex(5);
855                break;
856            case SignalHead.FLASHLUNAR:
857                box.setSelectedIndex(6);
858                break;
859            case SignalHead.FLASHYELLOW:
860                box.setSelectedIndex(7);
861                break;
862            case SignalHead.FLASHGREEN:
863                box.setSelectedIndex(8);
864                break;
865            default:
866                log.error("unexpected Signal state value: {}", state);
867        }
868    }
869
870    private void setTurnoutStateInBox(JComboBox<String> box, int state, int[] iTurnoutStates) {
871        if (state == iTurnoutStates[0]) {
872            box.setSelectedIndex(0);
873        } else if (state == iTurnoutStates[1]) {
874            box.setSelectedIndex(1);
875        } else {
876            log.error("unexpected turnout state value: {}", state);
877        }
878    }
879
880    private int turnoutStateFromBox(JComboBox<String> box) {
881        String mode = (String) box.getSelectedItem();
882        int result = StringUtil.getStateFromName(mode, TURNOUT_STATE_VALUES, TURNOUT_STATE_STRINGS);
883        if (result < 0) {
884            log.warn("unexpected mode string in turnoutMode: {}", mode);
885            throw new IllegalArgumentException();
886        }
887        return result;
888    }
889
890    private void setSignalheadTypeInBox(JComboBox<String> box, int state, int[] iSignalheadTypes) {
891        if (state == iSignalheadTypes[0]) {
892            box.setSelectedIndex(0);
893        } else if (state == iSignalheadTypes[1]) {
894            box.setSelectedIndex(1);
895        } else if (state == iSignalheadTypes[2]) {
896            box.setSelectedIndex(2);
897        } else if (state == iSignalheadTypes[3]) {
898            box.setSelectedIndex(3);
899        } else {
900            log.error("unexpected signalhead type value: {}", state);
901        }
902    }
903
904    private int signalStateFromBox(JComboBox<String> box) {
905        String mode = (String) box.getSelectedItem();
906        int result = StringUtil.getStateFromName(mode, SIGNAL_STATE_VALUES, SIGNAL_STATE_STRINGS);
907
908        if (result < 0) {
909            log.warn("unexpected mode string in signalMode: {}", mode);
910            throw new IllegalArgumentException();
911        }
912        return result;
913    }
914
915    private int ukSignalAspectsFromBox(JComboBox<String> box) {
916        //String mode = (String)box.getSelectedItem();
917        switch (box.getSelectedIndex()) {
918            case 0:
919                return 2;
920            case 1:
921                return 3;
922            case 2:
923                return 4;
924            default:
925                log.warn("unexpected appearance{}", box.getSelectedItem());
926                throw new IllegalArgumentException();
927        }
928    }
929
930    private void setUkSignalAspectsFromBox(JComboBox<String> box, int val) {
931        switch (val) {
932            case 2:
933                box.setSelectedIndex(0);
934                break;
935            case 3:
936                box.setSelectedIndex(1);
937                break;
938            case 4:
939                box.setSelectedIndex(2);
940                break;
941            default:
942                log.error("Unexpected Signal Appearance{}", val);
943                break;
944        }
945    }
946
947    private String ukSignalTypeFromBox(JComboBox<String> box) {
948        //String mode = (String)box.getSelectedItem();
949        switch (box.getSelectedIndex()) {
950            case 0:
951                return "Home"; // NOI18N
952            case 1:
953                return "Distant"; // NOI18N
954            default:
955                log.warn("unexpected appearance{}", box.getSelectedItem());
956                throw new IllegalArgumentException();
957        }
958    }
959
960    private void setUkSignalType(JComboBox<String> box, String val) {
961        if (val.equals(UK_SEMAPHORE_TYPES[0])) {
962            box.setSelectedIndex(0);
963        } else if (val.equals(UK_SEMAPHORE_TYPES[1])) {
964            box.setSelectedIndex(1);
965        } else {
966            log.error("Unexpected Signal Type {}", val);
967        }
968    }
969
970    private void ukAspectChange() {
971        switch (ukSignalAspectsFromBox(numberUkAspectsBox)) {
972            case 2:
973                centrePanel2.setVisible(true);
974                centrePanel4.setVisible(false);
975                turnoutSelect4.setVisible(false);
976                centrePanel5.setVisible(false);
977                turnoutSelect5.setVisible(false);
978                ukSignalSemaphoreTypeBox.setVisible(true);
979                break;
980            case 3:
981                centrePanel2.setVisible(false);
982                centrePanel4.setVisible(true);
983                turnoutSelect4.setVisible(true);
984                centrePanel5.setVisible(false);
985                turnoutSelect5.setVisible(false);
986                ukSignalSemaphoreTypeBox.setVisible(false);
987                setUkSignalType(ukSignalSemaphoreTypeBox, Bundle.getMessage("HomeSignal"));
988                break;
989            case 4:
990                centrePanel2.setVisible(false);
991                centrePanel4.setVisible(true);
992                turnoutSelect4.setVisible(true);
993                centrePanel5.setVisible(true);
994                turnoutSelect5.setVisible(true);
995                ukSignalSemaphoreTypeBox.setVisible(false);
996                setUkSignalType(ukSignalSemaphoreTypeBox, Bundle.getMessage("HomeSignal"));
997                break;
998            default:
999                break;
1000        }
1001        pack();
1002    }
1003
1004    @SuppressWarnings("fallthrough")
1005    @SuppressFBWarnings(value = "SF_SWITCH_FALLTHROUGH")
1006    private void updateEditPressed(ActionEvent e) {
1007        log.debug("newedit {}", e);
1008        if ( signalHeadBeingEdited==null ){
1009            createNewSigHead();
1010            return;
1011        }
1012        String nam = userNameField.getText();
1013        // check if user name changed
1014        String uname = signalHeadBeingEdited.getUserName();
1015        // TODO: not sure this if statement is right. I think (uname != null && !uname.equals(nam))
1016        if (!((uname != null) && (uname.equals(nam)))) {
1017            if (checkUserName(nam)) {
1018                signalHeadBeingEdited.setUserName(nam);
1019            } else {
1020                return;
1021            }
1022        }
1023        // update according to class of signal head
1024        String className = signalHeadBeingEdited.getClass().getName();
1025        switch (className) {
1026            case "jmri.implementation.QuadOutputSignalHead": {
1027                var headType = ((QuadOutputSignalHead) signalHeadBeingEdited).getGreen();
1028                Turnout t1 = updateTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":Green",
1029                    (headType==null ? null : headType.getBean()), centrePanBorder1.getTitle());
1030
1031                if (t1 == null) {
1032                    return;
1033                } else {
1034                    ((QuadOutputSignalHead) signalHeadBeingEdited).setGreen(nbhm.getNamedBeanHandle(turnoutSelect1.getDisplayName(), t1));
1035                }
1036
1037                headType = ((QuadOutputSignalHead) signalHeadBeingEdited).getYellow();
1038                Turnout t2 = updateTurnoutFromPanel(turnoutSelect2, "SignalHead:" + systemNameField.getText() + ":Yellow",
1039                    (headType==null ? null : headType.getBean()), centrePanBorder2.getTitle());
1040                if (t2 == null) {
1041                    return;
1042                } else {
1043                    ((QuadOutputSignalHead) signalHeadBeingEdited).setYellow(nbhm.getNamedBeanHandle(turnoutSelect2.getDisplayName(), t2));
1044                }
1045
1046                headType = ((QuadOutputSignalHead) signalHeadBeingEdited).getRed();
1047                Turnout t3 = updateTurnoutFromPanel(turnoutSelect3, "SignalHead:" + systemNameField.getText() + ":Red",
1048                    (headType==null ? null : headType.getBean()), centrePanBorder3.getTitle());
1049                if (t3 == null) {
1050                    return;
1051                } else {
1052                    ((QuadOutputSignalHead) signalHeadBeingEdited).setRed(nbhm.getNamedBeanHandle(turnoutSelect3.getDisplayName(), t3));
1053                }
1054
1055                headType = ((QuadOutputSignalHead) signalHeadBeingEdited).getLunar();
1056                Turnout t4 = updateTurnoutFromPanel(turnoutSelect4, "SignalHead:" + systemNameField.getText() + ":Lunar",
1057                    (headType==null ? null : headType.getBean()), centrePanBorder4.getTitle());
1058                if (t4 == null) {
1059                    return;
1060                } else {
1061                    ((QuadOutputSignalHead) signalHeadBeingEdited).setLunar(nbhm.getNamedBeanHandle(turnoutSelect4.getDisplayName(), t4));
1062                }
1063                break;
1064            }
1065            case "jmri.implementation.TripleTurnoutSignalHead": {
1066
1067                var headType = ((TripleTurnoutSignalHead) signalHeadBeingEdited).getGreen();
1068                Turnout t1 = updateTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":Green",
1069                    (headType==null ? null : headType.getBean()), centrePanBorder1.getTitle());
1070                if (t1 == null) {
1071                    return;
1072                } else {
1073                    ((TripleTurnoutSignalHead) signalHeadBeingEdited).setGreen(nbhm.getNamedBeanHandle(turnoutSelect1.getDisplayName(), t1));
1074                }
1075
1076                headType = ((TripleTurnoutSignalHead) signalHeadBeingEdited).getYellow();
1077                Turnout t2 = updateTurnoutFromPanel(turnoutSelect2, "SignalHead:" + systemNameField.getText() + ":Yellow",
1078                    (headType==null ? null : headType.getBean()), centrePanBorder2.getTitle());
1079                if (t2 == null) {
1080                    return;
1081                } else {
1082                    ((TripleTurnoutSignalHead) signalHeadBeingEdited).setYellow(nbhm.getNamedBeanHandle(turnoutSelect2.getDisplayName(), t2));
1083                }
1084
1085                headType = ((TripleTurnoutSignalHead) signalHeadBeingEdited).getRed();
1086                Turnout t3 = updateTurnoutFromPanel(turnoutSelect3, "SignalHead:" + systemNameField.getText() + ":Red",
1087                    (headType==null ? null : headType.getBean()), centrePanBorder3.getTitle());
1088                if (t3 == null) {
1089                    return;
1090                } else {
1091                    ((TripleTurnoutSignalHead) signalHeadBeingEdited).setRed(nbhm.getNamedBeanHandle(turnoutSelect3.getDisplayName(), t3));
1092                }
1093                break;
1094            }
1095            case "jmri.implementation.TripleOutputSignalHead": {
1096
1097                var headType = ((TripleOutputSignalHead) signalHeadBeingEdited).getGreen();
1098                Turnout t1 = updateTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":Green",
1099                    (headType==null ? null : headType.getBean()), centrePanBorder1.getTitle());
1100                if (t1 == null) {
1101                    return;
1102                } else {
1103                    ((TripleOutputSignalHead) signalHeadBeingEdited).setGreen(nbhm.getNamedBeanHandle(turnoutSelect1.getDisplayName(), t1));
1104                }
1105
1106                headType = ((TripleOutputSignalHead) signalHeadBeingEdited).getBlue();
1107                Turnout t2 = updateTurnoutFromPanel(turnoutSelect2, "SignalHead:" + systemNameField.getText() + ":Blue",
1108                    (headType==null ? null : headType.getBean()), centrePanBorder2.getTitle());
1109                if (t2 == null) {
1110                    return;
1111                } else {
1112                    ((TripleOutputSignalHead) signalHeadBeingEdited).setBlue(nbhm.getNamedBeanHandle(turnoutSelect2.getDisplayName(), t2));
1113                }
1114
1115                headType = ((TripleOutputSignalHead) signalHeadBeingEdited).getRed();
1116                Turnout t3 = updateTurnoutFromPanel(turnoutSelect3, "SignalHead:" + systemNameField.getText() + ":Red",
1117                    (headType==null ? null : headType.getBean()), centrePanBorder3.getTitle());
1118                if (t3 == null) {
1119                    return;
1120                } else {
1121                    ((TripleOutputSignalHead) signalHeadBeingEdited).setRed(nbhm.getNamedBeanHandle(turnoutSelect3.getDisplayName(), t3));
1122                }
1123                break;
1124            }
1125            case "jmri.implementation.DoubleTurnoutSignalHead": {
1126                var headType = ((DoubleTurnoutSignalHead) signalHeadBeingEdited).getGreen();
1127                Turnout t1 = updateTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":Green",
1128                    (headType==null ? null : headType.getBean()), centrePanBorder1.getTitle());
1129                headType = ((DoubleTurnoutSignalHead) signalHeadBeingEdited).getRed();
1130                Turnout t2 = updateTurnoutFromPanel(turnoutSelect2, "SignalHead:" + systemNameField.getText() + ":Red",
1131                    (headType==null ? null : headType.getBean()), centrePanBorder2.getTitle());
1132                if (t1 == null) {
1133                    return;
1134                } else {
1135                    ((DoubleTurnoutSignalHead) signalHeadBeingEdited).setGreen(nbhm.getNamedBeanHandle(turnoutSelect1.getDisplayName(), t1));
1136                }
1137                if (t2 == null) {
1138                    return;
1139                } else {
1140                    ((DoubleTurnoutSignalHead) signalHeadBeingEdited).setRed(nbhm.getNamedBeanHandle(turnoutSelect2.getDisplayName(), t2));
1141                }
1142                break;
1143            }
1144            case "jmri.implementation.SingleTurnoutSignalHead": {
1145                var headType = ((SingleTurnoutSignalHead) signalHeadBeingEdited).getOutput();
1146                Turnout t1 = updateTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":" + signalStateBox2.getSelectedItem() + ":" + signalStateBox3.getSelectedItem(),
1147                    (headType==null ? null : headType.getBean()), centrePanBorder1.getTitle());
1148                if (t1 == null) {
1149                    noTurnoutMessage(centrePanBorder1.getTitle(), turnoutSelect1.getDisplayName());
1150                    return;
1151                }
1152                ((SingleTurnoutSignalHead) signalHeadBeingEdited).setOutput(nbhm.getNamedBeanHandle(turnoutSelect1.getDisplayName(), t1));
1153                ((SingleTurnoutSignalHead) signalHeadBeingEdited).setOnAppearance(signalStateFromBox(signalStateBox2));
1154                ((SingleTurnoutSignalHead) signalHeadBeingEdited).setOffAppearance(signalStateFromBox(signalStateBox3));
1155                break;
1156            }
1157            case "jmri.implementation.LsDecSignalHead": {
1158                var headType = ((LsDecSignalHead) signalHeadBeingEdited).getGreen();
1159                Turnout t1 = updateTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":Green",
1160                    (headType==null ? null : headType.getBean()), centrePanBorder1.getTitle());
1161                if (t1 == null) {
1162                    return;
1163                } else {
1164                    ((LsDecSignalHead) signalHeadBeingEdited).setGreen(nbhm.getNamedBeanHandle(turnoutSelect1.getDisplayName(), t1));
1165                    ((LsDecSignalHead) signalHeadBeingEdited).setGreenState(turnoutStateFromBox(turnoutStateBox1));
1166                }
1167
1168                headType = ((LsDecSignalHead) signalHeadBeingEdited).getYellow();
1169                Turnout t2 = updateTurnoutFromPanel(turnoutSelect2, "SignalHead:" + systemNameField.getText() + ":Yellow",
1170                    (headType==null ? null : headType.getBean()), centrePanBorder2.getTitle());
1171                if (t2 == null) {
1172                    return;
1173                } else {
1174                    ((LsDecSignalHead) signalHeadBeingEdited).setYellow(nbhm.getNamedBeanHandle(turnoutSelect2.getDisplayName(), t2));
1175                    ((LsDecSignalHead) signalHeadBeingEdited).setYellowState(turnoutStateFromBox(turnoutStateBox2));
1176                }
1177
1178                headType = ((LsDecSignalHead) signalHeadBeingEdited).getRed();
1179                Turnout t3 = updateTurnoutFromPanel(turnoutSelect3, "SignalHead:" + systemNameField.getText() + ":Red",
1180                    (headType==null ? null : headType.getBean()), centrePanBorder3.getTitle());
1181                if (t3 == null) {
1182                    return;
1183                } else {
1184                    ((LsDecSignalHead) signalHeadBeingEdited).setRed(nbhm.getNamedBeanHandle(turnoutSelect3.getDisplayName(), t3));
1185                    ((LsDecSignalHead) signalHeadBeingEdited).setRedState(turnoutStateFromBox(turnoutStateBox3));
1186                }
1187
1188                headType = ((LsDecSignalHead) signalHeadBeingEdited).getFlashGreen();
1189                Turnout t4 = updateTurnoutFromPanel(turnoutSelect4, "SignalHead:" + systemNameField.getText() + ":FlashGreen",
1190                    (headType==null ? null : headType.getBean()), centrePanBorder4.getTitle());
1191                if (t4 == null) {
1192                    return;
1193                } else {
1194                    ((LsDecSignalHead) signalHeadBeingEdited).setFlashGreen(nbhm.getNamedBeanHandle(turnoutSelect4.getDisplayName(), t4));
1195                    ((LsDecSignalHead) signalHeadBeingEdited).setFlashGreenState(turnoutStateFromBox(turnoutStateBox4));
1196                }
1197
1198                headType = ((LsDecSignalHead) signalHeadBeingEdited).getFlashYellow();
1199                Turnout t5 = updateTurnoutFromPanel(turnoutSelect5, "SignalHead:" + systemNameField.getText() + ":FlashYellow",
1200                    (headType==null ? null : headType.getBean()), centrePanBorder5.getTitle());
1201                if (t5 == null) {
1202                    return;
1203                } else {
1204                    ((LsDecSignalHead) signalHeadBeingEdited).setFlashYellow(nbhm.getNamedBeanHandle(turnoutSelect5.getDisplayName(), t5));
1205                    ((LsDecSignalHead) signalHeadBeingEdited).setFlashYellowState(turnoutStateFromBox(turnoutStateBox5));
1206                }
1207
1208                headType = ((LsDecSignalHead) signalHeadBeingEdited).getFlashRed();
1209                Turnout t6 = updateTurnoutFromPanel(turnoutSelect6, "SignalHead:" + systemNameField.getText() + ":FlashRed",
1210                    (headType==null ? null : headType.getBean()), centrePanBorder6.getTitle());
1211                if (t6 == null) {
1212                    return;
1213                } else {
1214                    ((LsDecSignalHead) signalHeadBeingEdited).setFlashRed(nbhm.getNamedBeanHandle(turnoutSelect6.getDisplayName(), t6));
1215                    ((LsDecSignalHead) signalHeadBeingEdited).setFlashRedState(turnoutStateFromBox(turnoutStateBox6));
1216                }
1217
1218                headType = ((LsDecSignalHead) signalHeadBeingEdited).getDark();
1219                Turnout t7 = updateTurnoutFromPanel(turnoutSelect7, "SignalHead:" + systemNameField.getText() + ":Dark",
1220                    (headType==null ? null : headType.getBean()), centrePanBorder7.getTitle());
1221                if (t7 == null) {
1222                    return;
1223                } else {
1224                    ((LsDecSignalHead) signalHeadBeingEdited).setDark(nbhm.getNamedBeanHandle(turnoutSelect7.getDisplayName(), t7));
1225                    ((LsDecSignalHead) signalHeadBeingEdited).setDarkState(turnoutStateFromBox(turnoutStateBox7));
1226                }
1227                break;
1228            }
1229            case "jmri.jmrix.acela.AcelaSignalHead":
1230                AcelaNode tNode = AcelaAddress.getNodeFromSystemName(signalHeadBeingEdited.getSystemName(), InstanceManager.getDefault(AcelaSystemConnectionMemo.class));
1231                if (tNode == null) {
1232                    // node does not exist, ignore call
1233                    log.error("Can't find new Acela Signal with name '{}'", signalHeadBeingEdited.getSystemName());
1234                    return;
1235                }
1236                int headnumber = Integer.parseInt(signalHeadBeingEdited.getSystemName().substring(2));
1237                tNode.setOutputSignalHeadTypeString(headnumber, Objects.requireNonNull(acelaHeadTypeBox.getSelectedItem()).toString());
1238                break;
1239            case "jmri.implementation.MergSD2SignalHead":
1240                switch (ukSignalAspectsFromBox(numberUkAspectsBox)) {
1241                    case 4:
1242                        var input3 = ((MergSD2SignalHead) signalHeadBeingEdited).getInput3();
1243                        Turnout t3 = updateTurnoutFromPanel(turnoutSelect5, (Bundle.getMessage("OutputComment",
1244                            Bundle.getMessage("BeanNameSignalHead"), systemNameField.getText(),
1245                            Bundle.getMessage("InputNum", "3"))), (input3==null ? null : input3.getBean()),
1246                                centrePanBorder5.getTitle());
1247                        if (t3 == null) {
1248                            return;
1249                        } else {
1250                            ((MergSD2SignalHead) signalHeadBeingEdited).setInput3(nbhm.getNamedBeanHandle(turnoutSelect5.getDisplayName(), t3));
1251                        }
1252                        // fall through
1253                    case 3:
1254                        var input2 = ((MergSD2SignalHead) signalHeadBeingEdited).getInput2();
1255                        Turnout t2 = updateTurnoutFromPanel(turnoutSelect4, (Bundle.getMessage("OutputComment",
1256                            Bundle.getMessage("BeanNameSignalHead"), systemNameField.getText(),
1257                            Bundle.getMessage("InputNum", "2"))), (input2==null ? null : input2.getBean()),
1258                                centrePanBorder4.getTitle());
1259                        if (t2 == null) {
1260                            return;
1261                        } else {
1262                            ((MergSD2SignalHead) signalHeadBeingEdited).setInput2(nbhm.getNamedBeanHandle(turnoutSelect4.getDisplayName(), t2));
1263                        }
1264                        // fall through
1265                    case 2:
1266                        var input1 = ((MergSD2SignalHead) signalHeadBeingEdited).getInput1();
1267                        Turnout t1 = updateTurnoutFromPanel(turnoutSelect3, (Bundle.getMessage("OutputComment",
1268                            Bundle.getMessage("BeanNameSignalHead"), systemNameField.getText(),
1269                            Bundle.getMessage("InputNum", "1"))), (input1==null ? null : input1.getBean()),
1270                                centrePanBorder3.getTitle());
1271                        if (t1 == null) {
1272                            return;
1273                        } else {
1274                            ((MergSD2SignalHead) signalHeadBeingEdited).setInput1(nbhm.getNamedBeanHandle(turnoutSelect3.getDisplayName(), t1));
1275                        }
1276                        ((MergSD2SignalHead) signalHeadBeingEdited).setAspects(ukSignalAspectsFromBox(numberUkAspectsBox));
1277                        ((MergSD2SignalHead) signalHeadBeingEdited).setHome(!ukSignalTypeFromBox(ukSignalSemaphoreTypeBox).equals("Distant"));
1278                        break;
1279                    default:
1280                        break;
1281                }
1282                break;
1283            case "jmri.implementation.DccSignalHead":
1284                for (int i = 0; i < dccAspectSpinners.length; i++) {
1285                    int number = (Integer) dccAspectSpinners[i].getValue();
1286                    try {
1287                        ((DccSignalHead) signalHeadBeingEdited).setOutputForAppearance(signalHeadBeingEdited.getValidStates()[i], number);
1288                    } catch (Exception ex) {
1289                        //in theory JSpinner should already have caught a number conversion error.
1290                        log.error("JSpinner for {} did not catch number conversion error", className, ex);
1291                    }
1292                }
1293                ((DccSignalHead) signalHeadBeingEdited).useAddressOffSet(dccOffSetAddressCheckBox.isSelected());
1294                ((DccSignalHead) signalHeadBeingEdited).setDccSignalHeadPacketSendCount(((int) dccPacketSendCountSpinner.getValue()));
1295                break;
1296            case "jmri.implementation.VirtualSignalHead":
1297            case "jmri.implementation.SE8cSignalHead":
1298            case "jmri.jmrix.grapevine.SerialSignalHead":
1299                break;
1300            default:
1301                log.error("Internal error - cannot update signal of type {}", className );
1302                break;
1303        }
1304        // successful
1305        dispose();
1306    }
1307
1308    private void createNewSigHead() {
1309        if (!checkUserName(userNameField.getText())) {
1310            return;
1311        }
1312        SignalHead s;
1313        try {
1314            if (SE8C4_ASPECT.equals(headTypeBox.getSelectedItem())) {
1315                handleSE8cOkPressed();
1316            } else if (ACELA_ASPECT.equals(headTypeBox.getSelectedItem())) {
1317                String inputusername = userNameField.getText();
1318                String inputsysname = systemNameField.getText();
1319                int headnumber;
1320
1321                if (inputsysname.length() == 0) {
1322                    JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("signalHeadEntryWarning"));
1323                    log.warn("must supply a signalhead number (i.e. AH23) using your prefix");
1324                    return;
1325                }
1326
1327                var acelaMemo = InstanceManager.getNullableDefault(AcelaSystemConnectionMemo.class);
1328                if ( acelaMemo == null ){
1329                    JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("SystemNotActiveWarning", "Acela"));
1330                    log.warn("No active Acela connection to create Signal Head");
1331                    return;
1332                }
1333
1334                String acelaPrefix = acelaMemo.getSystemPrefix();
1335                if (inputsysname.length() > 2) {
1336                    int offset = Manager.getSystemPrefixLength(inputsysname);
1337                    if (inputsysname.startsWith(acelaPrefix)) {
1338                        headnumber = Integer.parseInt(inputsysname.substring(offset));
1339                    } else if (checkIntegerOnly(inputsysname)) {
1340                        headnumber = Integer.parseInt(inputsysname);
1341                    } else {
1342                        log.warn("skipping creation of signal head, '{}' does not start with AxH", inputsysname);
1343                        String msg = Bundle.getMessage("acelaSkippingCreation", systemNameField.getText());
1344                        JmriJOptionPane.showMessageDialog(this, msg,
1345                                Bundle.getMessage("WarningTitle"), JmriJOptionPane.ERROR_MESSAGE);
1346                        return;
1347                    }
1348                } else {
1349                    headnumber = Integer.parseInt(inputsysname);
1350                }
1351
1352                AcelaNode acelaNode = AcelaAddress.getNodeFromSystemName(acelaPrefix + "H" + headnumber, InstanceManager.getDefault(AcelaSystemConnectionMemo.class));
1353                if (acelaNode==null) {
1354                    JmriJOptionPane.showMessageDialog(this,
1355                        Bundle.getMessage("acelaNoNodeFound",Bundle.getMessage("BeanNameSignalHead"),headnumber),
1356                        Bundle.getMessage("ErrorSignalHeadAddFailed",headnumber), JmriJOptionPane.ERROR_MESSAGE);
1357                    return;
1358                }
1359
1360                if (checkSysNameOkBeforeCreating(acelaPrefix + "H" + headnumber)) {
1361                    if (inputusername.length() == 0) {
1362                        s = new AcelaSignalHead(acelaPrefix + "H" + headnumber, InstanceManager.getDefault(AcelaSystemConnectionMemo.class));
1363                    } else {
1364                        s = new AcelaSignalHead(acelaPrefix + "H" + headnumber, inputusername, InstanceManager.getDefault(AcelaSystemConnectionMemo.class));
1365                    }
1366                    InstanceManager.getDefault(SignalHeadManager.class).register(s);
1367                }
1368
1369                int st = acelaSignalheadTypeFromBox(acelaHeadTypeBox);
1370                switch (st) {
1371                    case 1:
1372                        acelaNode.setOutputSignalHeadType(headnumber, AcelaNode.DOUBLE);
1373                        break;
1374                    case 2:
1375                        acelaNode.setOutputSignalHeadType(headnumber, AcelaNode.TRIPLE);
1376                        break;
1377                    case 3:
1378                        acelaNode.setOutputSignalHeadType(headnumber, AcelaNode.BPOLAR);
1379                        break;
1380                    case 4:
1381                        acelaNode.setOutputSignalHeadType(headnumber, AcelaNode.WIGWAG);
1382                        break;
1383                    default:
1384                        log.warn("Unexpected Acela Aspect type: {}", st);
1385                        acelaNode.setOutputSignalHeadType(headnumber, AcelaNode.UKNOWN);
1386                        break;  // default to triple
1387                }
1388
1389            } else if (GRAPEVINE.equals(headTypeBox.getSelectedItem())) {
1390                // the turnout field must hold a GxH system name (Gx = multichar prefix)
1391                if (systemNameField.getText().length() == 0) {
1392                    JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("signalHeadEntryWarning"));
1393                    log.warn("must supply a signalhead number (i.e. GH23) using your prefix");
1394                    return;
1395                }
1396                String inputsysname = systemNameField.getText();
1397                int offset = Manager.getSystemPrefixLength(inputsysname);
1398                String grapevinePrefix = InstanceManager.getDefault(GrapevineSystemConnectionMemo.class).getSystemPrefix();
1399                if (!inputsysname.startsWith(grapevinePrefix) || inputsysname.charAt(offset) != 'H') {
1400                    log.warn("skipping creation of signal head, '{}' does not start with GxH", inputsysname);
1401                    String msg = Bundle.getMessage("GrapevineSkippingCreation", inputsysname);
1402                    JmriJOptionPane.showMessageDialog(this, msg,
1403                            Bundle.getMessage("WarningTitle"), JmriJOptionPane.ERROR_MESSAGE);
1404                    return;
1405                }
1406                if (checkSysNameOkBeforeCreating(inputsysname)) {
1407                    s = new SerialSignalHead(inputsysname, userNameField.getText(), InstanceManager.getDefault(GrapevineSystemConnectionMemo.class));
1408                    InstanceManager.getDefault(SignalHeadManager.class).register(s);
1409                }
1410            } else if (QUAD_OUTPUT.equals(headTypeBox.getSelectedItem())) {
1411                if (checkSysNameOkBeforeCreating(systemNameField.getText())) {
1412                    Turnout t1 = getTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":Green");
1413                    Turnout t2 = getTurnoutFromPanel(turnoutSelect2, "SignalHead:" + systemNameField.getText() + ":Yellow");
1414                    Turnout t3 = getTurnoutFromPanel(turnoutSelect3, "SignalHead:" + systemNameField.getText() + ":Red");
1415                    Turnout t4 = getTurnoutFromPanel(turnoutSelect4, "SignalHead:" + systemNameField.getText() + ":Lunar");
1416
1417                    if (t1 == null) {
1418                        addTurnoutMessage(centrePanBorder1.getTitle(), turnoutSelect1.getDisplayName());
1419                    }
1420                    if (t2 == null) {
1421                        addTurnoutMessage(centrePanBorder2.getTitle(), turnoutSelect2.getDisplayName());
1422                    }
1423                    if (t3 == null) {
1424                        addTurnoutMessage(centrePanBorder3.getTitle(), turnoutSelect3.getDisplayName());
1425                    }
1426                    if (t4 == null) {
1427                        addTurnoutMessage(centrePanBorder4.getTitle(), turnoutSelect4.getDisplayName());
1428                    }
1429                    if (t4 == null || t3 == null || t2 == null || t1 == null) {
1430                        log.warn("skipping creation of signal {} due to error", systemNameField.getText());
1431                        return;
1432                    }
1433                    s = new QuadOutputSignalHead(systemNameField.getText(), userNameField.getText(),
1434                            nbhm.getNamedBeanHandle(turnoutSelect1.getDisplayName(), t1),
1435                            nbhm.getNamedBeanHandle(turnoutSelect2.getDisplayName(), t2),
1436                            nbhm.getNamedBeanHandle(turnoutSelect3.getDisplayName(), t3),
1437                            nbhm.getNamedBeanHandle(turnoutSelect4.getDisplayName(), t4));
1438                    InstanceManager.getDefault(SignalHeadManager.class).register(s);
1439
1440                }
1441            } else if (TRIPLE_TURNOUT.equals(headTypeBox.getSelectedItem())) {
1442                if (checkSysNameOkBeforeCreating(systemNameField.getText())) {
1443                    Turnout t1 = getTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":Green");
1444                    Turnout t2 = getTurnoutFromPanel(turnoutSelect2, "SignalHead:" + systemNameField.getText() + ":Yellow");
1445                    Turnout t3 = getTurnoutFromPanel(turnoutSelect3, "SignalHead:" + systemNameField.getText() + ":Red");
1446
1447                    if (t1 == null) {
1448                        addTurnoutMessage(centrePanBorder1.getTitle(), turnoutSelect1.getDisplayName());
1449                    }
1450                    if (t2 == null) {
1451                        addTurnoutMessage(centrePanBorder2.getTitle(), turnoutSelect2.getDisplayName());
1452                    }
1453                    if (t3 == null) {
1454                        addTurnoutMessage(centrePanBorder3.getTitle(), turnoutSelect3.getDisplayName());
1455                    }
1456                    if (t3 == null || t2 == null || t1 == null) {
1457                        log.warn("skipping creation of signal {} due to error", systemNameField.getText());
1458                        return;
1459                    }
1460
1461                    s = new TripleTurnoutSignalHead(systemNameField.getText(), userNameField.getText(),
1462                            nbhm.getNamedBeanHandle(turnoutSelect1.getDisplayName(), t1),
1463                            nbhm.getNamedBeanHandle(turnoutSelect2.getDisplayName(), t2),
1464                            nbhm.getNamedBeanHandle(turnoutSelect3.getDisplayName(), t3));
1465                    InstanceManager.getDefault(SignalHeadManager.class).register(s);
1466                }
1467            } else if (TRIPLE_OUTPUT.equals(headTypeBox.getSelectedItem())) {
1468                if (checkSysNameOkBeforeCreating(systemNameField.getText())) {
1469                    Turnout t1 = getTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":Green");
1470                    Turnout t2 = getTurnoutFromPanel(turnoutSelect2, "SignalHead:" + systemNameField.getText() + ":Blue");
1471                    Turnout t3 = getTurnoutFromPanel(turnoutSelect3, "SignalHead:" + systemNameField.getText() + ":Red");
1472
1473                    if (t1 == null) {
1474                        addTurnoutMessage(centrePanBorder1.getTitle(), turnoutSelect1.getDisplayName());
1475                    }
1476                    if (t2 == null) {
1477                        addTurnoutMessage(centrePanBorder2.getTitle(), turnoutSelect2.getDisplayName());
1478                    }
1479                    if (t3 == null) {
1480                        addTurnoutMessage(centrePanBorder3.getTitle(), turnoutSelect3.getDisplayName());
1481                    }
1482                    if (t3 == null || t2 == null || t1 == null) {
1483                        log.warn("skipping creation of signal {} due to error", systemNameField.getText());
1484                        return;
1485                    }
1486
1487                    s = new TripleOutputSignalHead(systemNameField.getText(), userNameField.getText(),
1488                            nbhm.getNamedBeanHandle(turnoutSelect1.getDisplayName(), t1),
1489                            nbhm.getNamedBeanHandle(turnoutSelect2.getDisplayName(), t2),
1490                            nbhm.getNamedBeanHandle(turnoutSelect3.getDisplayName(), t3));
1491                    InstanceManager.getDefault(SignalHeadManager.class).register(s);
1492                }
1493            } else if (DOUBLE_TURNOUT.equals(headTypeBox.getSelectedItem())) {
1494                if (checkSysNameOkBeforeCreating(systemNameField.getText())) {
1495                    Turnout t1 = getTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":Green");
1496                    Turnout t2 = getTurnoutFromPanel(turnoutSelect2, "SignalHead:" + systemNameField.getText() + ":Red");
1497
1498                    if (t1 == null) {
1499                        addTurnoutMessage(centrePanBorder1.getTitle(), turnoutSelect1.getDisplayName());
1500                    }
1501                    if (t2 == null) {
1502                        addTurnoutMessage(centrePanBorder2.getTitle(), turnoutSelect2.getDisplayName());
1503                    }
1504                    if (t2 == null || t1 == null) {
1505                        log.warn("skipping creation of signal {} due to error", systemNameField.getText());
1506                        return;
1507                    }
1508
1509                    s = new DoubleTurnoutSignalHead(systemNameField.getText(), userNameField.getText(),
1510                            nbhm.getNamedBeanHandle(turnoutSelect1.getDisplayName(), t1),
1511                            nbhm.getNamedBeanHandle(turnoutSelect2.getDisplayName(), t2));
1512                    InstanceManager.getDefault(SignalHeadManager.class).register(s);
1513                }
1514            } else if (SINGLE_TURNOUT.equals(headTypeBox.getSelectedItem())) {
1515                if (checkSysNameOkBeforeCreating(systemNameField.getText())) {
1516                    Turnout t1 = getTurnoutFromPanel(turnoutSelect1,
1517                            "SignalHead:" + systemNameField.getText() + ":" + signalStateBox2.getSelectedItem() + ":" + signalStateBox3.getSelectedItem());
1518
1519                    int on = signalStateFromBox(signalStateBox2);
1520                    int off = signalStateFromBox(signalStateBox3);
1521                    if (t1 == null) {
1522                        addTurnoutMessage(centrePanBorder1.getTitle(), turnoutSelect1.getDisplayName());
1523                    }
1524                    if (t1 == null) {
1525                        log.warn("skipping creation of signal {} due to error", systemNameField.getText());
1526                        return;
1527                    }
1528
1529                    s = new SingleTurnoutSignalHead(systemNameField.getText(), userNameField.getText(),
1530                            nbhm.getNamedBeanHandle(t1.getDisplayName(), t1), on, off);
1531                    InstanceManager.getDefault(SignalHeadManager.class).register(s);
1532                }
1533            } else if (VIRTUAL_HEAD.equals(headTypeBox.getSelectedItem())) {
1534                if (checkSysNameOkBeforeCreating(systemNameField.getText())) {
1535                    s = new VirtualSignalHead(systemNameField.getText(), userNameField.getText());
1536                    InstanceManager.getDefault(SignalHeadManager.class).register(s);
1537                }
1538            } else if (LSDEC.equals(headTypeBox.getSelectedItem())) { // LDT LS-DEC
1539                if (checkSysNameOkBeforeCreating(systemNameField.getText())) {
1540                    Turnout t1 = getTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":Green");
1541                    Turnout t2 = getTurnoutFromPanel(turnoutSelect2, "SignalHead:" + systemNameField.getText() + ":Yellow");
1542                    Turnout t3 = getTurnoutFromPanel(turnoutSelect3, "SignalHead:" + systemNameField.getText() + ":Red");
1543                    Turnout t4 = getTurnoutFromPanel(turnoutSelect4, "SignalHead:" + systemNameField.getText() + ":FlashGreen");
1544                    Turnout t5 = getTurnoutFromPanel(turnoutSelect5, "SignalHead:" + systemNameField.getText() + ":FlashYellow");
1545                    Turnout t6 = getTurnoutFromPanel(turnoutSelect6, "SignalHead:" + systemNameField.getText() + ":FlashRed");
1546                    Turnout t7 = getTurnoutFromPanel(turnoutSelect7, "SignalHead:" + systemNameField.getText() + ":Dark");
1547
1548                    int s1 = turnoutStateFromBox(turnoutStateBox1);
1549                    int s2 = turnoutStateFromBox(turnoutStateBox2);
1550                    int s3 = turnoutStateFromBox(turnoutStateBox3);
1551                    int s4 = turnoutStateFromBox(turnoutStateBox4);
1552                    int s5 = turnoutStateFromBox(turnoutStateBox5);
1553                    int s6 = turnoutStateFromBox(turnoutStateBox6);
1554                    int s7 = turnoutStateFromBox(turnoutStateBox7);
1555
1556                    if (t1 == null) {
1557                        addTurnoutMessage(centrePanBorder1.getTitle(), turnoutSelect1.getDisplayName());
1558                    }
1559                    if (t2 == null) {
1560                        addTurnoutMessage(centrePanBorder2.getTitle(), turnoutSelect2.getDisplayName());
1561                    }
1562                    if (t3 == null) {
1563                        addTurnoutMessage(centrePanBorder3.getTitle(), turnoutSelect3.getDisplayName());
1564                    }
1565                    if (t4 == null) {
1566                        addTurnoutMessage(centrePanBorder4.getTitle(), turnoutSelect4.getDisplayName());
1567                    }
1568                    if (t5 == null) {
1569                        addTurnoutMessage(centrePanBorder5.getTitle(), turnoutSelect5.getDisplayName());
1570                    }
1571                    if (t6 == null) {
1572                        addTurnoutMessage(centrePanBorder6.getTitle(), turnoutSelect6.getDisplayName());
1573                    }
1574                    if (t7 == null) {
1575                        addTurnoutMessage(centrePanBorder7.getTitle(), turnoutSelect7.getDisplayName());
1576                    }
1577                    if (t7 == null || t6 == null || t5 == null || t4 == null || t3 == null || t2 == null || t1 == null) {
1578                        log.warn("skipping creation of signal {} due to error", systemNameField.getText());
1579                        return;
1580                    }
1581                    s = new LsDecSignalHead(systemNameField.getText(),
1582                            nbhm.getNamedBeanHandle(t1.getDisplayName(), t1), s1,
1583                            nbhm.getNamedBeanHandle(t2.getDisplayName(), t2), s2,
1584                            nbhm.getNamedBeanHandle(t3.getDisplayName(), t3), s3,
1585                            nbhm.getNamedBeanHandle(t4.getDisplayName(), t4), s4,
1586                            nbhm.getNamedBeanHandle(t5.getDisplayName(), t5), s5,
1587                            nbhm.getNamedBeanHandle(t6.getDisplayName(), t6), s6,
1588                            nbhm.getNamedBeanHandle(t7.getDisplayName(), t7), s7);
1589                    s.setUserName(userNameField.getText());
1590                    InstanceManager.getDefault(SignalHeadManager.class).register(s);
1591                }
1592            } else if (DCC_SIGNAL_DECODER.equals(headTypeBox.getSelectedItem())) {
1593                handleDCCOkPressed();
1594            } else if (MERG_SIGNAL_DRIVER.equals(headTypeBox.getSelectedItem())) {
1595                handleMergSignalDriverOkPressed();
1596            } else {
1597                throw new UnsupportedOperationException("Unexpected type: " + headTypeBox.getSelectedItem());
1598            }
1599
1600        } catch (NumberFormatException ex) {
1601            handleCreateException(ex, systemNameField.getText());
1602            // return; // without creating
1603        }
1604
1605    }
1606
1607    private void addTurnoutMessage(String s1, String s2) {
1608        log.warn("Could not provide turnout {}", s2);
1609        String msg = Bundle.getMessage("AddNoTurnout", s1, s2);
1610        JmriJOptionPane.showMessageDialog(this, msg,
1611                Bundle.getMessage("WarningTitle") + " " + s1, JmriJOptionPane.ERROR_MESSAGE);
1612    }
1613
1614    private void handleCreateException(Exception ex, String sysName) {
1615        if (ex.getLocalizedMessage() != null) {
1616            JmriJOptionPane.showMessageDialog(this,
1617                    ex.getLocalizedMessage(),
1618                    Bundle.getMessage("ErrorTitle"),
1619                    JmriJOptionPane.ERROR_MESSAGE);
1620        } else if (ex.getMessage() != null) {
1621            JmriJOptionPane.showMessageDialog(this,
1622                    ex.getMessage(),
1623                    Bundle.getMessage("ErrorTitle"),
1624                    JmriJOptionPane.ERROR_MESSAGE);
1625        } else {
1626            JmriJOptionPane.showMessageDialog(this,
1627                    Bundle.getMessage("ErrorSignalHeadAddFailed", sysName) + "\n" + Bundle.getMessage("ErrorAddFailedCheck"),
1628                    Bundle.getMessage("ErrorTitle"),
1629                    JmriJOptionPane.ERROR_MESSAGE);
1630        }
1631    }
1632
1633    private int acelaSignalheadTypeFromBox(JComboBox<String> box) {
1634        String mode = (String) box.getSelectedItem();
1635        int result = StringUtil.getStateFromName(mode, ACELA_SIG_HEAD_TYPE_VALUES, ACELA_SIG_HEAD_TYPES);
1636
1637        if (result < 0) {
1638            log.warn("unexpected mode string in signalhead appearance type: {}", mode);
1639            throw new IllegalArgumentException();
1640        }
1641        return result;
1642    }
1643
1644    private void handleSE8cOkPressed() {
1645
1646        Turnout t1 = getTurnoutFromPanel(turnoutSelect1, "SignalHead:" + systemNameField.getText() + ":low");
1647        Turnout t2 = getTurnoutFromPanel(turnoutSelect2, "SignalHead:" + systemNameField.getText() + ":high");
1648
1649        // check validity
1650        if (t1 != null && t2 != null) {
1651            // OK, process
1652            SignalHead s;
1653            try {
1654                s = new SE8cSignalHead(
1655                        nbhm.getNamedBeanHandle(t1.getSystemName(), t1),
1656                        nbhm.getNamedBeanHandle(t2.getSystemName(), t2),
1657                        userNameField.getText());
1658            } catch (NumberFormatException ex) {
1659                // user input no good
1660                handleCreate2TurnoutException(t1.getSystemName(),
1661                        t2.getSystemName(), userNameField.getText());
1662                return; // without creating any
1663            }
1664            try {
1665                InstanceManager.getDefault(SignalHeadManager.class).register(s);
1666            } catch ( jmri.NamedBean.DuplicateSystemNameException ex) {
1667                s.dispose();
1668                JmriJOptionPane.showMessageDialog(this,"<html>"
1669                    + Bundle.getMessage("ErrorSe8cDuplicateSysName", t1.getDisplayName(), t2.getDisplayName())
1670                    + "<br>" + Bundle.getMessage("ErrorReplaceHead")
1671                    + "<br>" + ex.getLocalizedMessage() + "</html>",
1672                    Bundle.getMessage("WarningTitle"), JmriJOptionPane.ERROR_MESSAGE);
1673            }
1674        } else {
1675            // couldn't create turnouts, error
1676            String msg;
1677            if (t1 == null) {
1678                msg = Bundle.getMessage("se8c4SkippingDueToErrorInFirst");
1679            } else {
1680                msg = Bundle.getMessage("se8c4SkippingDueToErrorInSecond");
1681            }
1682            JmriJOptionPane.showMessageDialog(this, msg,
1683                    Bundle.getMessage("WarningTitle"), JmriJOptionPane.ERROR_MESSAGE);
1684        }
1685    }
1686
1687    private void handleDCCOkPressed() {
1688        DccSignalHead s;
1689        String systemNameText = null;
1690        String prefix = (String) prefixBox.getSelectedItem();
1691        if (prefix != null) {
1692            systemNameText = ConnectionNameFromSystemName.getPrefixFromName(prefix);
1693        }
1694        // if we return a null string then we will set it to use internal, thus picking up the default command station at a later date.
1695        if (systemNameText == null) {
1696            systemNameText = "I";
1697        }
1698        systemNameText = systemNameText + "H$" + systemNameField.getText();
1699
1700        if (checkSysNameOkBeforeCreating(systemNameText)) {
1701            s = new DccSignalHead(systemNameText);
1702            s.setUserName(userNameField.getText());
1703            log.debug("dccAspect Length = {}", dccAspectSpinners.length);
1704            for (int i = 0; i < DccSignalHead.getDefaultValidStates().length; i++) { // no need to check DCC ID input when using JSpinner
1705                log.debug("i = {}", i);
1706                int number = (Integer) dccAspectSpinners[i].getValue();
1707                try {
1708                    s.setOutputForAppearance(s.getValidStates()[i], number);
1709                } catch (RuntimeException ex) {
1710                    log.warn("error setting \"{}\" output for appearance \"{}\"", systemNameText, number);
1711                }
1712            }
1713            InstanceManager.getDefault(SignalHeadManager.class).register(s);
1714            s.useAddressOffSet(dccOffSetAddressCheckBox.isSelected());
1715            s.setDccSignalHeadPacketSendCount((int)dccPacketSendCountSpinner.getValue());
1716        }
1717    }
1718
1719    @SuppressWarnings("fallthrough")
1720    @SuppressFBWarnings(value = "SF_SWITCH_FALLTHROUGH")
1721    private void handleMergSignalDriverOkPressed() {
1722        SignalHead s;
1723        // Adding Merg Signal Driver.
1724        Turnout t3;
1725        Turnout t2;
1726        Turnout t1;
1727        NamedBeanHandle<Turnout> nbt1 = null;
1728        NamedBeanHandle<Turnout> nbt2 = null;
1729        NamedBeanHandle<Turnout> nbt3 = null;
1730        if (checkSysNameOkBeforeCreating(systemNameField.getText())) {
1731            switch (ukSignalAspectsFromBox(numberUkAspectsBox)) {
1732                case 4:
1733                    t3 = getTurnoutFromPanel(turnoutSelect5,
1734                            (Bundle.getMessage("OutputComment", Bundle.getMessage("BeanNameSignalHead"), systemNameField.getText(), Bundle.getMessage("InputNum", "3"))));
1735                    if (t3 == null) {
1736                        addTurnoutMessage(centrePanBorder5.getTitle(), turnoutSelect5.getDisplayName());
1737                        log.warn("skipping creation of signal {} due to error", systemNameField.getText());
1738                        return;
1739                    } else {
1740                        nbt3 = nbhm.getNamedBeanHandle(turnoutSelect5.getDisplayName(), t3);
1741                    }
1742
1743                // fall through
1744                case 3:
1745                    t2 = getTurnoutFromPanel(turnoutSelect4,
1746                            (Bundle.getMessage("OutputComment", Bundle.getMessage("BeanNameSignalHead"), systemNameField.getText(), Bundle.getMessage("InputNum", "2"))));
1747                    if (t2 == null) {
1748                        addTurnoutMessage(centrePanBorder4.getTitle(), turnoutSelect4.getDisplayName());
1749                        log.warn("skipping creation of signal {} due to error", systemNameField.getText());
1750                        return;
1751                    } else {
1752                        nbt2 = nbhm.getNamedBeanHandle(turnoutSelect4.getDisplayName(), t2);
1753                    }
1754                // fall through
1755                case 2:
1756                    t1 = getTurnoutFromPanel(turnoutSelect3,
1757                            (Bundle.getMessage("OutputComment", Bundle.getMessage("BeanNameSignalHead"), systemNameField.getText(), Bundle.getMessage("InputNum", "1"))));
1758                    if (t1 == null) {
1759                        addTurnoutMessage(centrePanBorder3.getTitle(), turnoutSelect3.getDisplayName());
1760                        log.warn("skipping creation of signal {} due to error", systemNameField.getText());
1761                        return;
1762                    } else {
1763                        nbt1 = nbhm.getNamedBeanHandle(turnoutSelect3.getDisplayName(), t1);
1764                    }
1765                    break;
1766                default:
1767                    break;
1768            }
1769            boolean home = !ukSignalTypeFromBox(ukSignalSemaphoreTypeBox).equals(Bundle.getMessage("DistantSignal"));
1770
1771            s = new MergSD2SignalHead(systemNameField.getText(), ukSignalAspectsFromBox(numberUkAspectsBox), nbt1, nbt2, nbt3, false, home);
1772            s.setUserName(userNameField.getText());
1773            InstanceManager.getDefault(SignalHeadManager.class).register(s);
1774
1775        }
1776    }
1777
1778    private boolean checkSysNameOkBeforeCreating(String sysName) {
1779        if (DCC_SIGNAL_DECODER.equals(headTypeBox.getSelectedItem())) {
1780            try {
1781                Integer.valueOf(sysName.substring(sysName.indexOf("$") + 1));
1782            } catch (NumberFormatException ex) {
1783                String msg = Bundle.getMessage("ShouldBeNumber", "Hardware Address");
1784                JmriJOptionPane.showMessageDialog(this, msg,
1785                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.ERROR_MESSAGE);
1786                return false;
1787            }
1788        } else {
1789            boolean ok = true;
1790            try {
1791                int i = Manager.getSystemPrefixLength(sysName);
1792                if (sysName.length() < i+2) {
1793                    ok = false;
1794                } else {
1795                    if (sysName.charAt(i) != 'H') ok = false;
1796                }
1797            } catch (NamedBean.BadSystemNameException e) {
1798                ok = false;
1799            }
1800            if (!ok) {
1801                String msg = Bundle.getMessage("InvalidSignalSystemName", sysName);
1802                JmriJOptionPane.showMessageDialog(this, msg,
1803                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.ERROR_MESSAGE);
1804                return false;
1805            }
1806        }
1807        // check for pre-existing signal head with same system name
1808        SignalHead s = InstanceManager.getDefault(SignalHeadManager.class).getBySystemName(sysName);
1809        // return true if signal head does not exist
1810        if (s == null) {
1811            //Need to check that the Systemname doesn't already exists as a UserName
1812            SignalHead nB = InstanceManager.getDefault(SignalHeadManager.class).getByUserName(sysName);
1813            if (nB != null) {
1814                log.error("System name is not unique {} It already exists as a User name", sysName);
1815                String msg = Bundle.getMessage("WarningSystemNameAsUser", ("" + sysName));
1816                JmriJOptionPane.showMessageDialog(this, msg,
1817                        Bundle.getMessage("WarningTitle"),
1818                        JmriJOptionPane.ERROR_MESSAGE);
1819                return false;
1820            }
1821            return true;
1822        }
1823        // inform the user if signal head already exists, and return false so creation can be bypassed
1824        log.warn("Attempt to create signal with duplicate system name {}", sysName);
1825        String msg = Bundle.getMessage("DuplicateSignalSystemName", sysName);
1826        JmriJOptionPane.showMessageDialog(this, msg,
1827                Bundle.getMessage("WarningTitle"), JmriJOptionPane.ERROR_MESSAGE);
1828        return false;
1829    }
1830
1831    private boolean checkIntegerOnly(String s) {
1832        for (int i = 0; i < s.length(); i++) {
1833            if (!Character.isDigit(s.charAt(i))) {
1834                return false;
1835            }
1836        }
1837        return true;
1838    }
1839
1840    private void handleCreate2TurnoutException(String t1, String t2, String uName) {
1841        JmriJOptionPane.showMessageDialog(this,
1842                Bundle.getMessage("ErrorSe8cAddFailed", uName, t1, t2) + "\n" + Bundle.getMessage("ErrorAddFailedCheck"),
1843                Bundle.getMessage("ErrorTitle"),
1844                JmriJOptionPane.ERROR_MESSAGE);
1845    }
1846
1847    /**
1848     * Update Turnout object for a signal mast output.
1849     *
1850     * @param bp         Pane in which the new output/bean was entered by user
1851     * @param reference  Turnout application description
1852     * @param oldTurnout Previously used output
1853     * @param title      for warning pane
1854     * @return The newly defined output as Turnout object
1855     */
1856    @CheckForNull
1857    private Turnout updateTurnoutFromPanel(@Nonnull BeanSelectCreatePanel<Turnout> bp, String reference, @CheckForNull Turnout oldTurnout, String title) {
1858        Turnout newTurnout = getTurnoutFromPanel(bp, reference);
1859        if (newTurnout == null) {
1860            noTurnoutMessage(title, bp.getDisplayName());
1861        }
1862        String comment;
1863        if (newTurnout != null) {
1864            comment = newTurnout.getComment();
1865            if  (comment == null || comment.isEmpty()) {
1866                newTurnout.setComment(reference); // enter turnout application description into new turnout Comment
1867            }
1868        }
1869        if (oldTurnout == null || newTurnout == oldTurnout) {
1870            return newTurnout;
1871        }
1872        comment = oldTurnout.getComment();
1873        if (comment != null && comment.equals(reference)) {
1874            // wont delete old Turnout Comment if Locale or Bundle was changed in between, but user could have type something in the Comment as well
1875            oldTurnout.setComment(null); // deletes current Comment in bean
1876        }
1877        return newTurnout;
1878    }
1879
1880    /**
1881     * Create Turnout object for a signal mast output.
1882     *
1883     * @param bp        Pane in which the new output/bean was entered by user
1884     * @param reference Turnout application description
1885     * @return The new output as Turnout object
1886     */
1887    @CheckForNull
1888    private Turnout getTurnoutFromPanel(@Nonnull BeanSelectCreatePanel<Turnout> bp, String reference) {
1889        bp.setReference(reference); // pass turnout application description to be put into turnout Comment
1890        try {
1891            return bp.getNamedBean();
1892        } catch (JmriException ex) {
1893            log.warn("skipping creation of turnout not found for {}", reference);
1894            return null;
1895        }
1896    }
1897
1898    private boolean checkUserName(String nam) {
1899        if (!((nam == null) || (nam.isEmpty()))) {
1900            // user name changed, check if new name already exists
1901            NamedBean nB = InstanceManager.getDefault(SignalHeadManager.class).getByUserName(nam);
1902            if (nB != null) {
1903                log.error("User name is not unique {}", nam);
1904                String msg = Bundle.getMessage("WarningUserName", ("" + nam));
1905                JmriJOptionPane.showMessageDialog(this, msg,
1906                        Bundle.getMessage("InvalidUserNameAlreadyExists", Bundle.getMessage("BeanNameSignalHead"),nam),
1907                        JmriJOptionPane.ERROR_MESSAGE);
1908                return false;
1909            }
1910            //Check to ensure that the username doesn't exist as a systemname.
1911            nB = InstanceManager.getDefault(SignalHeadManager.class).getBySystemName(nam);
1912            if (nB != null) {
1913                log.error("User name is not unique {} It already exists as a System name", nam);
1914                String msg = Bundle.getMessage("WarningUserNameAsSystem", ("" + nam));
1915                JmriJOptionPane.showMessageDialog(this, msg,
1916                        Bundle.getMessage("InvalidUserNameAlreadyExists", Bundle.getMessage("BeanNameSignalHead"),nam),
1917                        JmriJOptionPane.ERROR_MESSAGE);
1918                return false;
1919            }
1920        }
1921        return true;
1922    }
1923
1924    private void noTurnoutMessage(String s1, String s2) {
1925        log.warn("Could not provide turnout {}", s2);
1926        String msg = Bundle.getMessage("WarningNoTurnout", s1, s2);
1927        JmriJOptionPane.showMessageDialog(this, msg,
1928                Bundle.getMessage("WarningTitle"), JmriJOptionPane.ERROR_MESSAGE);
1929    }
1930
1931    @Override
1932    public void dispose() {
1933        if (turnoutSelect1 != null) {
1934            turnoutSelect1.dispose();
1935        }
1936        if (turnoutSelect2 != null) {
1937            turnoutSelect2.dispose();
1938        }
1939        if (turnoutSelect3 != null) {
1940            turnoutSelect3.dispose();
1941        }
1942        if (turnoutSelect4 != null) {
1943            turnoutSelect4.dispose();
1944        }
1945        if (turnoutSelect5 != null) {
1946            turnoutSelect5.dispose();
1947        }
1948        if (turnoutSelect6 != null) {
1949            turnoutSelect6.dispose();
1950        }
1951        if (turnoutSelect7 != null) {
1952            turnoutSelect7.dispose();
1953        }
1954        super.dispose();
1955    }
1956
1957    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SignalHeadAddEditFrame.class);
1958
1959}