001package jmri.jmrit.beantable;
002
003import com.alexandriasoftware.swing.Validation;
004
005import java.awt.*;
006import java.awt.event.ActionEvent;
007import java.beans.PropertyChangeListener;
008import java.util.Objects;
009
010import javax.annotation.Nonnull;
011import javax.swing.*;
012import javax.swing.border.Border;
013
014import jmri.*;
015import jmri.jmrit.beantable.light.LightControlPane;
016import jmri.jmrit.beantable.light.LightIntensityPane;
017import jmri.jmrit.beantable.light.LightTableDataModel;
018import jmri.NamedBean.DisplayOptions;
019import jmri.swing.ManagerComboBox;
020import jmri.swing.SystemNameValidator;
021import jmri.util.JmriJFrame;
022import jmri.util.swing.JmriJOptionPane;
023
024/**
025 * Swing action to create and register a LightTable GUI.
026 * <p>
027 * Based on SignalHeadTableAction.java
028 *
029 * @author Dave Duchamp Copyright (C) 2004
030 * @author Egbert Broerse Copyright (C) 2017
031 */
032public class LightTableAction extends AbstractTableAction<Light> {
033
034    /**
035     * Create an action with a specific title.
036     * <p>
037     * Note that the argument is the Action title, not the title of the
038     * resulting frame. Perhaps this should be changed?
039     *
040     * @param s title of the action
041     */
042    public LightTableAction(String s) {
043        super(s);
044        // disable ourself if there is no primary Light manager available
045        if (lightManager == null) {
046            super.setEnabled(false);
047        }
048    }
049
050    public LightTableAction() {
051        this(Bundle.getMessage("TitleLightTable"));
052    }
053
054    protected LightManager lightManager = InstanceManager.getNullableDefault(jmri.LightManager.class);
055
056    /**
057     * {@inheritDoc}
058     */
059    @Override
060    public void setManager(@Nonnull Manager<Light> man) {
061        if (man instanceof LightManager) {
062            lightManager = (LightManager) man;
063        }
064    }
065
066    /**
067     * Create the JTable DataModel, along with the changes for the specific case
068     * of Lights.
069     */
070    @Override
071    protected void createModel() {
072        m = new LightTableDataModel(lightManager);
073    }
074
075    /**
076     * {@inheritDoc}
077     */
078    @Override
079    protected void setTitle() {
080        f.setTitle(Bundle.getMessage("TitleLightTable"));
081    }
082
083    /**
084     * {@inheritDoc}
085     */
086    @Override
087    protected String helpTarget() {
088        return "package.jmri.jmrit.beantable.LightTable";
089    }
090
091    JmriJFrame addFrame = null;
092    Light curLight = null;
093    boolean lightCreatedOrUpdated = false;
094    boolean noWarn = false;
095
096    // items for Add/Edit Light frame
097    JLabel systemLabel = new JLabel(Bundle.getMessage("SystemConnectionLabel"));
098    ManagerComboBox<Light> prefixBox = new ManagerComboBox<>();
099    JCheckBox addRangeBox = new JCheckBox(Bundle.getMessage("AddRangeBox"));
100    JTextField hardwareAddressTextField = new JTextField(10);
101    SystemNameValidator hardwareAddressValidator;
102    SpinnerNumberModel rangeSpinner = new SpinnerNumberModel(1, 1, 50, 1); // maximum 50 items
103    JSpinner numberToAdd = new JSpinner(rangeSpinner);
104    JLabel labelNumToAdd = new JLabel("   " + Bundle.getMessage("LabelNumberToAdd"));
105    String systemSelectionCombo = this.getClass().getName() + ".SystemSelected";
106
107    JLabel systemNameLabel = new JLabel(Bundle.getMessage("LabelSystemName") + " ");
108    JTextField userName = new JTextField(20);
109    JLabel userNameLabel = new JLabel(Bundle.getMessage("LabelUserName") + " ");
110    JButton create;
111    JButton cancel = new JButton(Bundle.getMessage("ButtonCancel"));
112    PropertyChangeListener colorChangeListener;
113
114    JLabel status1 = new JLabel(Bundle.getMessage("LightCreateInst"));
115    JLabel status2 = new JLabel("");
116    Manager<Light> connectionChoice = null;
117    private LightIntensityPane lightIntensityPanel;
118    private LightControlPane lightControlPanel;
119
120    private boolean checkEnabled = jmri.InstanceManager.getDefault(jmri.configurexml.ShutdownPreferences.class).isStoreCheckEnabled();
121
122    /**
123     * {@inheritDoc}
124     */
125    @Override
126    protected void addPressed(ActionEvent e) {
127        cancelPressed(null);
128
129        if (addFrame == null) {
130            addFrame = new JmriJFrame(Bundle.getMessage("TitleAddLight"), false, true);
131            addFrame.addHelpMenu("package.jmri.jmrit.beantable.LightAddEdit", true);
132            addFrame.setLocation(100, 30);
133            Container contentPane = addFrame.getContentPane();
134            contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
135            JPanel panel1 = new JPanel();
136            panel1.setLayout(new FlowLayout());
137            configureManagerComboBox(prefixBox, lightManager, LightManager.class);
138            panel1.add(systemLabel);
139            panel1.add(prefixBox);
140            panel1.add(new JLabel("   "));
141            panel1.add(addRangeBox);
142            addRangeBox.setVisible(true); // reset after Edit Light
143            addRangeBox.setToolTipText(Bundle.getMessage("LightAddRangeHint"));
144            addRangeBox.addActionListener((ActionEvent e1) -> addRangeChanged());
145            panel1.add(systemNameLabel);
146            systemNameLabel.setVisible(false);
147            prefixBox.setToolTipText(Bundle.getMessage("LightSystemHint"));
148            prefixBox.addActionListener((ActionEvent e1) -> prefixChanged());
149            contentPane.add(panel1);
150            JPanel hardwareAddressPanel = new JPanel();
151            hardwareAddressPanel.setLayout(new FlowLayout());
152            hardwareAddressPanel.add(new JLabel(Bundle.getMessage("LabelHardwareAddress")));
153            hardwareAddressPanel.add(hardwareAddressTextField);
154            hardwareAddressTextField.setText(""); // reset from possible previous use
155            hardwareAddressTextField.setToolTipText(Bundle.getMessage("LightHardwareAddressHint"));
156            hardwareAddressTextField.setName("hwAddressTextField"); // for GUI test NOI18N
157
158            if (hardwareAddressValidator==null){
159                hardwareAddressValidator = new SystemNameValidator(hardwareAddressTextField, Objects.requireNonNull(prefixBox.getSelectedItem()), true);
160            } else {
161                hardwareAddressValidator.setManager(prefixBox.getSelectedItem());
162            }
163
164            hardwareAddressTextField.setInputVerifier(hardwareAddressValidator);
165            prefixBox.addActionListener((evt) -> hardwareAddressValidator.setManager(prefixBox.getSelectedItem()));
166            hardwareAddressValidator.addPropertyChangeListener("validation", (evt) -> { // NOI18N
167                Validation validation = hardwareAddressValidator.getValidation();
168                Validation.Type type = validation.getType();
169                create.setEnabled(type != Validation.Type.WARNING && type != Validation.Type.DANGER);
170                String message = validation.getMessage();
171                if (message == null) {
172                    status1.setText("");
173                } else {
174                    message = message.trim();
175                    if (message.startsWith("<html>") && message.contains("<br>")) {
176                        message = message.substring(0, message.indexOf("<br>"));
177                        if (!message.endsWith("</html>")) {
178                            message = message + "</html>";
179                        }
180                    }
181                    status1.setText(message);
182                }
183            });
184            // tooltip and entry mask for sysNameTextField will be assigned later by prefixChanged()
185            hardwareAddressPanel.add(labelNumToAdd);
186            hardwareAddressPanel.add(numberToAdd);
187            numberToAdd.setToolTipText(Bundle.getMessage("LightNumberToAddHint"));
188            contentPane.add(hardwareAddressPanel);
189            JPanel panel2 = new JPanel();
190            panel2.setLayout(new FlowLayout());
191            panel2.add(userNameLabel);
192            panel2.add(userName);
193            userName.setText(""); // reset from possible previous use
194            userName.setToolTipText(Bundle.getMessage("LightUserNameHint"));
195            userName.setName("userName"); // for GUI test NOI18N
196            prefixBox.setName("prefixBox"); // for GUI test NOI18N
197            contentPane.add(panel2);
198            lightIntensityPanel = new LightIntensityPane(false);
199            Border varPanelTitled = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
200                Bundle.getMessage("LightVariableBorder"));
201            lightIntensityPanel.setBorder(varPanelTitled);
202            contentPane.add(lightIntensityPanel);
203            // light control table
204            lightControlPanel = new LightControlPane();
205            Border panel3Titled = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
206                Bundle.getMessage("LightControllerTitlePlural"));
207            lightControlPanel.setBorder(panel3Titled);
208            contentPane.add(lightControlPanel);
209            // message items
210            JPanel panel4 = new JPanel();
211            panel4.setLayout(new BoxLayout(panel4, BoxLayout.Y_AXIS));
212            JPanel panel41 = new JPanel();
213            panel41.setLayout(new FlowLayout());
214            // add status bar above buttons
215            panel41.add(status1);
216            status1.setText(Bundle.getMessage("LightCreateInst"));
217            status1.setFont(status1.getFont().deriveFont(0.9f * systemNameLabel.getFont().getSize())); // a bit smaller
218            status1.setForeground(Color.gray);
219            JPanel panel42 = new JPanel();
220            panel42.setLayout(new FlowLayout());
221            panel42.add(status2);
222            status2.setText(Bundle.getMessage("LightCreateInst"));
223            status2.setFont(status2.getFont().deriveFont(0.9f * systemNameLabel.getFont().getSize())); // a bit smaller
224            status2.setForeground(Color.gray);
225            status2.setText("");
226            status2.setVisible(false);
227            panel4.add(panel41);
228            panel4.add(panel42);
229            contentPane.add(panel4);
230            // buttons at bottom of window
231            JPanel panel5 = new JPanel();
232            panel5.setLayout(new FlowLayout(FlowLayout.TRAILING));
233            panel5.add(cancel);
234            cancel.setText(Bundle.getMessage("ButtonCancel"));
235            cancel.addActionListener(this::cancelPressed);
236            cancel.setToolTipText(Bundle.getMessage("LightCancelButtonHint"));
237            panel5.add(create = new JButton(Bundle.getMessage("ButtonCreate")));
238            create.addActionListener(this::createPressed);
239            create.setToolTipText(Bundle.getMessage("LightCreateButtonHint"));
240            create.setName("createButton"); // for GUI test NOI18N
241            create.setVisible(true);
242            contentPane.add(panel5);
243            hardwareAddressValidator.verify(hardwareAddressTextField);
244        }
245        prefixChanged();
246        addFrame.addWindowListener(new java.awt.event.WindowAdapter() {
247            @Override
248            public void windowClosing(java.awt.event.WindowEvent e) {
249                cancelPressed(null);
250            }
251        });
252        create.setEnabled(false); // start as disabled (false) until a valid entry is typed in
253        // reset statusBar text
254        status1.setText(Bundle.getMessage("LightCreateInst"));
255        status1.setForeground(Color.gray);
256
257        addFrame.setEscapeKeyClosesWindow(true);
258        addFrame.getRootPane().setDefaultButton(create);
259
260        addFrame.pack();
261        addFrame.setVisible(true);
262    }
263
264    private String addEntryToolTip;
265
266    protected void prefixChanged() {
267        if (prefixBox.getSelectedItem() != null) {
268            lightIntensityPanel.setVisible(supportsVariableLights());
269            // behaves like the AddNewHardwareDevice pane (dim if not available, do not hide)
270            addRangeBox.setEnabled(canAddRange());
271            addRangeBox.setSelected(false);
272            numberToAdd.setValue(1);
273            numberToAdd.setEnabled(false);
274            labelNumToAdd.setEnabled(false);
275            // show tooltip for selected system connection
276            connectionChoice = prefixBox.getSelectedItem(); // store in Field for CheckedTextField
277            // Update tooltip in the Add Light pane to match system connection selected from combobox.
278            log.debug("Connection choice = [{}]", connectionChoice);
279            // get tooltip from ProxyLightManager
280            String systemPrefix = connectionChoice.getSystemPrefix();
281            addEntryToolTip = connectionChoice.getEntryToolTip();
282            addRangeBox.setEnabled(((LightManager) connectionChoice).allowMultipleAdditions(systemPrefix));
283            log.debug("DefaultLightManager tip: {}", addEntryToolTip);
284            // show Hardware address field tooltip in the Add Light pane to match system connection selected from combobox
285            if (addEntryToolTip != null) {
286                hardwareAddressTextField.setToolTipText(
287                        Bundle.getMessage("AddEntryToolTipLine1",
288                                connectionChoice.getMemo().getUserName(),
289                                Bundle.getMessage("Lights"),
290                                addEntryToolTip));
291                hardwareAddressValidator.setToolTipText(hardwareAddressTextField.getToolTipText());
292                hardwareAddressValidator.verify(hardwareAddressTextField);
293            }
294            create.setEnabled(true); // too severe to start as disabled (false) until we fully support validation
295            addFrame.pack();
296            addFrame.setVisible(true);
297        }
298    }
299
300    protected void addRangeChanged() {
301        numberToAdd.setEnabled(addRangeBox.isSelected());
302        labelNumToAdd.setEnabled(addRangeBox.isSelected());
303    }
304
305    /**
306     * Activate Add a range option if manager accepts adding more than 1 Light.
307     * TODO: Will only verify against formats which accept "11" as a Hardware address.
308     */
309    private boolean canAddRange() {
310        String testSysName = Objects.requireNonNull(prefixBox.getSelectedItem()).getSystemPrefix() + "L11";
311        return lightManager.allowMultipleAdditions(testSysName);
312    }
313
314    /**
315     * Check if LightManager supports variable Lights.
316     * TODO: Will only verify against formats which accept "11" as a Hardware address.
317     *
318     * @return true if system can support variable lights.
319     */
320    boolean supportsVariableLights() {
321        String testSysName = Objects.requireNonNull(prefixBox.getSelectedItem()).getSystemPrefix() + "L11";
322        return lightManager.supportsVariableLights(testSysName);
323    }
324
325    /**
326     * Create lights when the Create New button on the Add/Create pane is
327     * pressed and entry is valid.
328     *
329     * @param e the button press action
330     */
331    void createPressed(ActionEvent e) {
332
333        status1.setForeground(Color.gray); // reset
334        status1.setText("");
335        String lightPrefix = Objects.requireNonNull(prefixBox.getSelectedItem()).getSystemPrefix() + "L";
336        String turnoutPrefix = prefixBox.getSelectedItem().getSystemPrefix() + "T";
337        String curAddress = hardwareAddressTextField.getText();
338        // first validation is provided by HardwareAddress ValidatedTextField on yield focus
339        if (curAddress.length() < 1) {
340            log.warn("Hardware Address was not entered");
341            status1.setText(Bundle.getMessage("LightError17"));
342            status1.setForeground(Color.red);
343            status2.setVisible(false);
344            hardwareAddressTextField.setBackground(Color.red);
345            addFrame.pack();
346            addFrame.setVisible(true);
347            return;
348        } else {
349            hardwareAddressTextField.setBackground(Color.white);
350        }
351        String suName = lightPrefix + curAddress;
352        String uName = userName.getText();
353        if (uName.isEmpty()) {
354            uName = null;   // a blank field means no user name
355        }
356        // Does System Name have a valid format?
357        if (InstanceManager.getDefault(LightManager.class).validSystemNameFormat(suName) != Manager.NameValidity.VALID) {
358            // Invalid System Name format
359            log.warn("Invalid Light system name format entered: {}", suName);
360            status1.setText(Bundle.getMessage("LightError3"));
361            status1.setForeground(Color.red);
362            status2.setText(Bundle.getMessage("LightError6"));
363            status2.setVisible(true);
364            hardwareAddressTextField.setBackground(Color.red);
365            addFrame.pack();
366            addFrame.setVisible(true);
367            return;
368        } else {
369            hardwareAddressTextField.setBackground(Color.white);
370        }
371        // check if a Light with this name already exists
372        Light g = InstanceManager.getDefault(LightManager.class).getBySystemName(suName);
373        if (g != null) {
374            // Light already exists
375            status1.setText(Bundle.getMessage("LightError1"));
376            status1.setForeground(Color.red);
377            status2.setText(Bundle.getMessage("LightError2"));
378            status2.setVisible(true);
379            addFrame.pack();
380            addFrame.setVisible(true);
381            return;
382        }
383        // check if Light exists under an alternate name if an alternate name exists
384        String altName = InstanceManager.getDefault(LightManager.class).convertSystemNameToAlternate(suName);
385        if (!altName.isEmpty()) {
386            g = InstanceManager.getDefault(LightManager.class).getBySystemName(altName);
387            if (g != null) {
388                // Light already exists
389                status1.setText(Bundle.getMessage("LightError10", altName));
390                status1.setForeground(Color.red);
391                status2.setVisible(false);
392                addFrame.pack();
393                addFrame.setVisible(true);
394                return;
395            }
396        }
397        // check if a Light with the same user name exists
398        if (uName != null) {
399            g = InstanceManager.getDefault(LightManager.class).getByUserName(uName);
400            if (g != null) {
401                // Light with this user name already exists
402                status1.setText(Bundle.getMessage("LightError8"));
403                status1.setForeground(Color.red);
404                status2.setText(Bundle.getMessage("LightError9"));
405                status2.setVisible(true);
406                addFrame.pack();
407                addFrame.setVisible(true);
408                return;
409            }
410        }
411        // check if System Name corresponds to configured hardware
412        if (!InstanceManager.getDefault(LightManager.class).validSystemNameConfig(suName)) {
413            // System Name not in configured hardware
414            status1.setText(Bundle.getMessage("LightError5"));
415            status1.setForeground(Color.red);
416            status2.setText(Bundle.getMessage("LightError6"));
417            status2.setVisible(true);
418            addFrame.pack();
419            addFrame.setVisible(true);
420            return;
421        }
422        // check if requested Light uses the same address as a Turnout
423        String testSN = turnoutPrefix + curAddress;
424        Turnout testT = InstanceManager.turnoutManagerInstance().
425                getBySystemName(testSN);
426        if (testT != null) {
427            // Address (number) is already used as a Turnout
428            log.warn("Requested Light {} uses same address as Turnout {}", suName, testT);
429            if (!noWarn) {
430                int selectedValue = JmriJOptionPane.showOptionDialog(addFrame,
431                        Bundle.getMessage("LightWarn5", suName, testSN),
432                        Bundle.getMessage("WarningTitle"),
433                        JmriJOptionPane.YES_NO_CANCEL_OPTION, JmriJOptionPane.QUESTION_MESSAGE, null,
434                        new Object[]{Bundle.getMessage("ButtonYes"), Bundle.getMessage("ButtonYesPlus"),
435                                Bundle.getMessage("ButtonNo")}, Bundle.getMessage("ButtonNo")); // default choice = No
436                if (selectedValue == 1) {
437                    return;   // return without creating on "No" response
438                }
439                if (selectedValue == 2) {
440                    // Suppress future warnings, and continue
441                    noWarn = true;
442                }
443            }
444            // Light with this system name address (number) already exists as a turnout
445            status2.setText(Bundle.getMessage("LightWarn4") + " " + testSN + ".");
446            status1.setForeground(Color.red);
447            status2.setVisible(true);
448        }
449
450        // Create a single new Light, or the first Light of a range
451        try {
452            g = InstanceManager.getDefault(LightManager.class).newLight(suName, uName);
453        } catch (IllegalArgumentException ex) {
454            // user input no good
455            handleCreateException(ex, suName);
456            return; // without creating
457        }
458        lightControlPanel.setLightFromControlTable(g);
459        if (g instanceof VariableLight) {
460            lightIntensityPanel.setLightFromPane((VariableLight)g);
461        }
462        g.activateLight();
463        lightCreatedOrUpdated = true;
464
465        status2.setText("");
466        status2.setVisible(false);
467
468        // provide feedback to user
469        String feedback = Bundle.getMessage("LightCreateFeedback") + " " + g.getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME);
470
471        // create additional lights if requested
472        int numberOfLights = 1;
473        if ((InstanceManager.getDefault(LightManager.class).allowMultipleAdditions(suName))
474               && addRangeBox.isSelected()) {
475           // get number requested
476          numberOfLights = (Integer) numberToAdd.getValue();
477        }
478        if (numberOfLights > 1) {
479            for (int i = 1; i < numberOfLights; i++) {
480
481                try {
482                    // bump system name
483                    suName = InstanceManager.getDefault(LightManager.class).getNextValidSystemName(g);
484
485                    // create it
486                    g = lightManager.newLight(suName, uName);
487                    log.debug("Light {} created",g);
488                    // set up this light the same as the first light
489                    lightControlPanel.setLightFromControlTable(g);
490                    if (g instanceof VariableLight) {
491                        lightIntensityPanel.setLightFromPane((VariableLight)g);
492                    }
493                } catch (IllegalArgumentException | JmriException ex) {
494                    // user input no good
495                    handleCreateException(ex, suName);
496                    return; // without creating any more Lights
497                }
498            }
499            feedback = feedback + " - " + suName + ", " + uName;
500        }
501        create.setEnabled(false);
502        status1.setText(feedback);
503        status1.setForeground(Color.gray);
504        cancel.setText(Bundle.getMessage("ButtonClose")); // when Create/Apply has been clicked at least once, this is not Revert/Cancel
505        addFrame.pack();
506        addFrame.setVisible(true);
507    }
508
509    void handleCreateException(Exception ex, String sysName) {
510        status1.setText(ex.getLocalizedMessage());
511        String err = Bundle.getMessage("ErrorBeanCreateFailed",
512            InstanceManager.getDefault(LightManager.class).getBeanTypeHandled(),sysName);
513        JmriJOptionPane.showMessageDialog(addFrame, err + "\n" + ex.getLocalizedMessage(),
514                err, JmriJOptionPane.ERROR_MESSAGE);
515    }
516
517    /**
518     * Respond to the Cancel/Close button on the Add/Edit Light pane.
519     *
520     * @param e the button press action
521     */
522    void cancelPressed(ActionEvent e) {
523
524        if (addFrame != null) {
525            addFrame.setVisible(false); // hide first for cleaner display
526        }
527
528        // remind to save, if Light was created or edited
529        if (lightCreatedOrUpdated && !checkEnabled) {
530            InstanceManager.getDefault(jmri.UserPreferencesManager.class).
531                    showInfoMessage(Bundle.getMessage("ReminderTitle"), Bundle.getMessage("ReminderSaveString",
532                            Bundle.getMessage("MenuItemLightTable")),
533                            getClassName(),
534                            "remindSaveLight"); // NOI18N
535        }
536        lightCreatedOrUpdated = false;
537        // finally, get rid of the add/edit Frame
538        if (addFrame != null) {
539
540            lightControlPanel.dispose(); // closes any popup windows
541
542            removePrefixBoxListener(prefixBox);
543            InstanceManager.getDefault(UserPreferencesManager.class).setComboBoxLastSelection(systemSelectionCombo, prefixBox.getSelectedItem().getMemo().getUserName()); // store user pref
544            addFrame.dispose();
545            addFrame = null;
546            create.removePropertyChangeListener(colorChangeListener);
547
548        }
549    }
550
551
552    // TODO: Move the next few static String methods to more appropriate place.
553    // LightControl.java ?  Multiple Bundle property files will need changing.
554
555    public static String lightControlTitle = Bundle.getMessage("LightControlBorder");
556
557    /**
558     * Get the description of the type of Light Control.
559     *
560     * @param lc   the light control
561     * @param type the type of lc
562     * @return description of the type of lc or an empty string if type is not
563     *         recognized
564     */
565    public static String getDescriptionText(LightControl lc, int type) {
566        switch (type) {
567            case Light.SENSOR_CONTROL:
568                return java.text.MessageFormat.format(Bundle.getMessage("LightSensorControlDes"), lc.getControlSensorName(), getControlSensorSenseText(lc));
569            case Light.FAST_CLOCK_CONTROL:
570                return java.text.MessageFormat.format(Bundle.getMessage("LightFastClockDes"),
571                        // build 00:00 from 2 fields
572                        String.format("%02d:%02d", lc.getFastClockOnHour(), lc.getFastClockOnMin()), String.format("%02d:%02d", lc.getFastClockOffHour(), lc.getFastClockOffMin()));
573            case Light.TURNOUT_STATUS_CONTROL:
574                return java.text.MessageFormat.format(Bundle.getMessage("LightTurnoutControlDes"), lc.getControlTurnoutName(), getControlTurnoutStateText(lc));
575            case Light.TIMED_ON_CONTROL:
576                return java.text.MessageFormat.format(Bundle.getMessage("LightTimedOnControlDes"), "" + lc.getTimedOnDuration(), lc.getControlTimedOnSensorName(), getControlSensorSenseText(lc));
577            case Light.TWO_SENSOR_CONTROL:
578                return java.text.MessageFormat.format(Bundle.getMessage("LightTwoSensorControlDes"), lc.getControlSensorName(), lc.getControlSensor2Name(), getControlSensorSenseText(lc));
579            default:
580                return "";
581        }
582    }
583
584    private static String getControlSensorSenseText(LightControl lc) {
585        if (lc.getControlSensorSense() == Sensor.ACTIVE) {
586            return Bundle.getMessage("SensorStateActive");
587        }
588        return Bundle.getMessage("SensorStateInactive");
589    }
590
591    private static String getControlTurnoutStateText(LightControl lc) {
592        if (lc.getControlTurnoutState() == Turnout.CLOSED) {
593            return InstanceManager.turnoutManagerInstance().getClosedText();
594        }
595        return InstanceManager.turnoutManagerInstance().getThrownText();
596    }
597
598    /**
599     * Validates that a physical turnout exists.
600     *
601     * @param inTurnoutName the (system or user) name of the turnout
602     * @param inOpenPane    the pane over which to show dialogs (null to
603     *                      suppress dialogs)
604     * @return true if valid turnout was entered, false otherwise
605     */
606    public boolean validatePhysicalTurnout(String inTurnoutName, Component inOpenPane) {
607        //check if turnout name was entered
608        if (inTurnoutName.isEmpty()) {
609            //no turnout entered
610            log.debug("no turnout was selected");
611            return false;
612        }
613        //check that the turnout name corresponds to a defined physical turnout
614        Turnout t = InstanceManager.turnoutManagerInstance().getTurnout(inTurnoutName);
615        if (t == null) {
616            //There is no turnout corresponding to this name
617            if (inOpenPane != null) {
618                JmriJOptionPane.showMessageDialog(inOpenPane,
619                        java.text.MessageFormat.format(Bundle.getMessage("LightWarn2"), inTurnoutName),
620                        Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
621            }
622            return false;
623        }
624        log.debug("validatePhysicalTurnout('{}')", inTurnoutName);
625        return true;
626    }
627
628    /**
629     * {@inheritDoc}
630     */
631    @Override
632    public String getClassDescription() {
633        return Bundle.getMessage("TitleLightTable");
634    }
635
636    /**
637     * {@inheritDoc}
638     */
639    @Override
640    protected String getClassName() {
641        return LightTableAction.class.getName();
642    }
643
644    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LightTableAction.class);
645
646}