001package jmri.jmrit.simplelightctrl;
002
003import java.awt.event.ActionEvent;
004import java.awt.event.ActionListener;
005import java.beans.PropertyChangeListener;
006import java.text.DecimalFormat;
007
008import javax.swing.BoxLayout;
009import javax.swing.JPanel;
010
011import jmri.*;
012import jmri.swing.NamedBeanComboBox;
013
014import org.slf4j.Logger;
015import org.slf4j.LoggerFactory;
016
017/**
018 * Frame controlling a single light.
019 * <p>
020 * Built from a copy of simple turnout control.
021 *
022 * @author Ken Cameron Copyright (C) 2008
023 * @author Bob Jacobsen Copyright (C) 2001, 2008
024 */
025public class SimpleLightCtrlFrame extends jmri.util.JmriJFrame {
026
027    DecimalFormat threeDigits = new DecimalFormat("000");
028    DecimalFormat oneDigits = new DecimalFormat("0");
029    DecimalFormat oneDotTwoDigits = new DecimalFormat("0.00");
030
031    Light light = null;
032    String newState = "";
033
034    // GUI member declarations
035    javax.swing.JButton onButton = new javax.swing.JButton();
036    javax.swing.JButton offButton = new javax.swing.JButton();
037
038    javax.swing.JLabel textStateLabel = new javax.swing.JLabel();
039    javax.swing.JLabel nowStateTextField = new javax.swing.JLabel();
040    javax.swing.JLabel nowControllersTextField = new javax.swing.JLabel();
041    javax.swing.JLabel textIsEnabledLabel = new javax.swing.JLabel();
042    javax.swing.JCheckBox statusIsEnabledCheckBox = new javax.swing.JCheckBox();
043    javax.swing.JLabel textIsVariableLabel = new javax.swing.JLabel();
044    javax.swing.JCheckBox statusIsVariableCheckBox = new javax.swing.JCheckBox();
045    javax.swing.JLabel textIsTransitionLabel = new javax.swing.JLabel();
046    javax.swing.JCheckBox statusIsTransitionCheckBox = new javax.swing.JCheckBox();
047
048    javax.swing.JLabel intensityTextLabel1 = new javax.swing.JLabel();
049    javax.swing.JLabel nowIntensityLabel = new javax.swing.JLabel();
050    javax.swing.JTextField intensityTextField = new javax.swing.JTextField(4);
051    javax.swing.JLabel intensityTextLabel2 = new javax.swing.JLabel();
052    javax.swing.JButton intensityButton = new javax.swing.JButton();
053
054    javax.swing.JLabel intensityMinTextLabel = new javax.swing.JLabel();
055    javax.swing.JLabel nowIntensityMinLabel = new javax.swing.JLabel();
056    javax.swing.JTextField intensityMinTextField = new javax.swing.JTextField(4);
057    javax.swing.JLabel intensityMaxTextLabel = new javax.swing.JLabel();
058    javax.swing.JLabel nowIntensityMaxLabel = new javax.swing.JLabel();
059    javax.swing.JTextField intensityMaxTextField = new javax.swing.JTextField(4);
060    javax.swing.JLabel transitionTimeTextLabel = new javax.swing.JLabel();
061    javax.swing.JLabel nowTransitionTimeLabel = new javax.swing.JLabel();
062    javax.swing.JTextField transitionTimeTextField = new javax.swing.JTextField(4);
063
064    javax.swing.JButton applyButton = new javax.swing.JButton();
065    private final NamedBeanComboBox<Light> to1;
066    private PropertyChangeListener _parentLightListener = null;
067
068    public SimpleLightCtrlFrame() {
069        super();
070
071        to1 = new NamedBeanComboBox<>(InstanceManager.lightManagerInstance());
072        to1.setAllowNull(true);
073        to1.addActionListener(new ActionListener() {
074            @Override
075            public void actionPerformed(ActionEvent e) {
076                log.debug("actionevent");
077                resetLightToCombo();
078            }
079        });
080
081        // configure items for GUI
082        textStateLabel.setText(Bundle.getMessage("LightStatusLabel"));
083        textStateLabel.setVisible(true);
084        nowStateTextField.setText(Bundle.getMessage("BeanStateUnknown"));
085        nowStateTextField.setVisible(true);
086        nowControllersTextField.setText("");
087        nowControllersTextField.setVisible(true);
088        textIsEnabledLabel.setText(Bundle.getMessage("LightIsEnabledLabel"));
089        textIsEnabledLabel.setToolTipText(Bundle.getMessage("LightIsEnabledLabelToolTip"));
090        textIsEnabledLabel.setVisible(true);
091        statusIsEnabledCheckBox.setVisible(true);
092        statusIsEnabledCheckBox.addActionListener(new ActionListener() {
093            @Override
094            public void actionPerformed(ActionEvent e) {
095                enabledCheckboxActionPerformed(e);
096            }
097        });
098        textIsVariableLabel.setText(Bundle.getMessage("LightIsVariableLabel"));
099        textIsVariableLabel.setVisible(true);
100        statusIsVariableCheckBox.setVisible(true);
101        statusIsVariableCheckBox.setEnabled(false);
102        textIsTransitionLabel.setText(Bundle.getMessage("LightIsTransitionLabel"));
103        textIsTransitionLabel.setVisible(true);
104        statusIsTransitionCheckBox.setVisible(true);
105        statusIsTransitionCheckBox.setEnabled(false);
106
107        onButton.setText(Bundle.getMessage("StateOn"));
108        onButton.setVisible(true);
109        onButton.setToolTipText(Bundle.getMessage("LightOnButtonToolTip"));
110        onButton.addActionListener(new ActionListener() {
111            @Override
112            public void actionPerformed(ActionEvent e) {
113                onButtonActionPerformed(e);
114            }
115        });
116
117        offButton.setText(Bundle.getMessage("StateOff"));
118        offButton.setVisible(true);
119        offButton.setToolTipText(Bundle.getMessage("LightOffButtonToolTip"));
120        offButton.addActionListener(new ActionListener() {
121            @Override
122            public void actionPerformed(ActionEvent e) {
123                offButtonActionPerformed(e);
124            }
125        });
126
127        intensityTextLabel1.setText(Bundle.getMessage("LightIntensityTextLabel"));
128        intensityTextLabel1.setVisible(true);
129        nowIntensityLabel.setText("");
130        nowIntensityLabel.setVisible(true);
131        intensityTextField.setText(oneDigits.format(0));
132        intensityTextField.setVisible(true);
133        intensityTextLabel2.setText("%");
134        intensityTextField.setToolTipText(Bundle.getMessage("LightIntensityTextToolTip"));
135
136        intensityMinTextLabel.setText(Bundle.getMessage("LightMinIntensityLabel"));
137        nowIntensityMinLabel.setText("");
138        nowIntensityMinLabel.setVisible(true);
139        intensityMinTextField.setText(oneDigits.format(0));
140        intensityMinTextField.setVisible(true);
141        intensityMinTextField.setToolTipText(Bundle.getMessage("LightMinIntensityToolTip"));
142        intensityMaxTextLabel.setText(Bundle.getMessage("LightMaxIntensityLabel"));
143        nowIntensityMaxLabel.setText("");
144        nowIntensityMaxLabel.setVisible(true);
145        intensityMaxTextField.setText(oneDigits.format(100));
146        intensityMaxTextField.setVisible(true);
147        intensityMaxTextField.setToolTipText(Bundle.getMessage("LightMinIntensityToolTip"));
148        transitionTimeTextLabel.setText(Bundle.getMessage("LightTransitionTimeLabel"));
149        nowTransitionTimeLabel.setText("");
150        nowTransitionTimeLabel.setVisible(true);
151        transitionTimeTextField.setText(oneDigits.format(0));
152        transitionTimeTextField.setVisible(true);
153        transitionTimeTextField.setEnabled(true);
154        transitionTimeTextField.setToolTipText(Bundle.getMessage("LightTransitionTimeToolTip"));
155        intensityButton.setText(Bundle.getMessage("LightSetButton"));
156        intensityButton.setVisible(true);
157        intensityButton.setToolTipText(Bundle.getMessage("LightSetButtonToolTip"));
158        intensityButton.addActionListener(new ActionListener() {
159            @Override
160            public void actionPerformed(ActionEvent e) {
161                intensityButtonActionPerformed(e);
162            }
163        });
164
165        applyButton.setText(Bundle.getMessage("ButtonApply"));
166        applyButton.setVisible(true);
167        applyButton.setToolTipText(Bundle.getMessage("LightApplyButtonToolTip"));
168        applyButton.addActionListener(new ActionListener() {
169            @Override
170            public void actionPerformed(ActionEvent e) {
171                applyButtonActionPerformed(e);
172            }
173        });
174
175        // set buttons inactive as no Light yet selected
176        setControlFrameActive(false);
177
178        // general GUI config
179        setTitle(Bundle.getMessage("LightBorder"));
180        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
181
182        // Light select
183        JPanel pane2 = new JPanel();
184        pane2.add(to1);
185        getContentPane().add(pane2);
186
187        // status text
188        pane2 = new JPanel();
189        pane2.add(textStateLabel);
190        pane2.add(nowStateTextField);
191        getContentPane().add(pane2);
192
193        // on off buttons
194        pane2 = new JPanel();
195        pane2.add(onButton);
196        pane2.add(offButton);
197        getContentPane().add(pane2);
198        getContentPane().add(new javax.swing.JSeparator(javax.swing.SwingConstants.HORIZONTAL));
199
200        // Controllers enabled checkbox
201        pane2 = new JPanel();
202        pane2.add(textIsEnabledLabel);
203        pane2.add(statusIsEnabledCheckBox);
204        getContentPane().add(pane2);
205
206        // Controllers text
207        pane2 = new JPanel();
208        pane2.add(nowControllersTextField);
209        getContentPane().add(pane2);
210        getContentPane().add(new javax.swing.JSeparator(javax.swing.SwingConstants.HORIZONTAL));
211
212        // intensity field and button
213        pane2 = new JPanel();
214        pane2.add(intensityTextLabel1);
215        pane2.add(nowIntensityLabel);
216        pane2.add(intensityTextField);
217        pane2.add(intensityTextLabel2);
218        pane2.add(intensityButton);
219        getContentPane().add(pane2);
220        getContentPane().add(new javax.swing.JSeparator(javax.swing.SwingConstants.HORIZONTAL));
221
222        // min max textfields
223        pane2 = new JPanel();
224        pane2.add(intensityMinTextLabel);
225        pane2.add(nowIntensityMinLabel);
226        pane2.add(intensityMinTextField);
227        pane2.add(intensityMaxTextLabel);
228        pane2.add(nowIntensityMaxLabel);
229        pane2.add(intensityMaxTextField);
230        getContentPane().add(pane2);
231
232        // time textfield, apply button
233        pane2 = new JPanel();
234        pane2.add(transitionTimeTextLabel);
235        pane2.add(nowTransitionTimeLabel);
236        pane2.add(transitionTimeTextField);
237        pane2.add(applyButton);
238        getContentPane().add(pane2);
239
240        // add help menu to window
241        addHelpMenu("package.jmri.jmrit.simplelightctrl.SimpleLightCtrl", true);
242
243        pack();
244
245    }
246
247    private void setControlFrameActive(boolean showLight) {
248        log.debug("selected light is {}", to1.getSelectedItem());
249        onButton.setEnabled(showLight);
250        offButton.setEnabled(showLight);
251        statusIsEnabledCheckBox.setEnabled(showLight);
252
253        if (showLight && (light instanceof VariableLight)) {
254            intensityButton.setEnabled(true);
255            intensityMinTextField.setEnabled(true);
256            intensityMaxTextField.setEnabled(true);
257            intensityTextField.setEnabled(true);
258            applyButton.setEnabled(true);
259        } else {
260            intensityButton.setEnabled(false);
261            intensityMinTextField.setEnabled(false);
262            intensityMaxTextField.setEnabled(false);
263            intensityTextField.setEnabled(false);
264            intensityButton.setEnabled(false);
265            applyButton.setEnabled(false);
266        }
267
268        if (showLight && (light instanceof VariableLight)
269                && ((VariableLight)light).isTransitionAvailable()) {
270            transitionTimeTextField.setEnabled(true);
271        } else {
272            transitionTimeTextField.setEnabled(false);
273        }
274
275    }
276
277    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT",
278                                                        justification="I18N of log message")
279    public void offButtonActionPerformed(ActionEvent e) {
280        if (to1.getSelectedItem() == null) {
281            nowStateTextField.setText(Bundle.getMessage("ErrorTitle"));
282            return;
283        }
284        try {
285            // and set commanded state to ON
286            light.setState(Light.OFF);
287        } catch (Exception ex) {
288            log.error("{}{}", Bundle.getMessage("ErrorTitle"), ex.toString());
289            nowStateTextField.setText(Bundle.getMessage("ErrorTitle"));
290        }
291    }
292
293    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT",
294                                                        justification="I18N of log message")
295    public void onButtonActionPerformed(ActionEvent e) {
296        if (to1.getSelectedItem() == null) {
297            nowStateTextField.setText(Bundle.getMessage("ErrorTitle"));
298            return;
299        }
300        try {
301            // and set commanded state to ON
302            light.setState(Light.ON);
303        } catch (Exception ex) {
304            log.error("{}{}", Bundle.getMessage("ErrorTitle"), ex.toString());
305            nowStateTextField.setText(Bundle.getMessage("ErrorTitle"));
306        }
307    }
308
309    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT",
310                                                        justification="I18N of log message")
311    public void intensityButtonActionPerformed(ActionEvent e) {
312        if (to1.getSelectedItem() == null) {
313            nowStateTextField.setText(Bundle.getMessage("ErrorTitle"));
314            return;
315        }
316        try {
317            log.debug("about to command DIM"); // NOI18N
318            // and set commanded state to DIM
319            if (light instanceof VariableLight) {
320                ((VariableLight)light).setTargetIntensity(
321                        Double.parseDouble(intensityTextField.getText().trim()) / 100);
322            }
323        } catch (NumberFormatException ex) {
324            log.error("{}{}", Bundle.getMessage("LightErrorIntensityButtonException"), ex.toString());
325            nowStateTextField.setText(Bundle.getMessage("ErrorTitle"));
326        }
327    }
328
329    private void enabledCheckboxActionPerformed(ActionEvent e) {
330        if (statusIsEnabledCheckBox.isSelected()) {
331            light.setEnabled(true);
332        } else {
333            light.setEnabled(false);
334        }
335    }
336
337    /**
338     * Handle changes for intensity, rate, etc.
339     * @param e unused.
340     */
341    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT",
342                                                        justification="I18N of log message")
343    public void applyButtonActionPerformed(ActionEvent e) {
344        if (to1.getSelectedItem() == null) {
345            nowStateTextField.setText(Bundle.getMessage("ErrorTitle"));
346            resetLightToCombo();
347            return;
348        }
349        // load address from switchAddrTextField
350        try {
351            if (light instanceof VariableLight) {
352                VariableLight variableLight = (VariableLight)light;
353                double min = Double.parseDouble(intensityMinTextField.getText()) / 100.;
354                double max = Double.parseDouble(intensityMaxTextField.getText()) / 100.;
355                double time = Double.parseDouble(transitionTimeTextField.getText());
356                log.debug("setting min: {} max: {} transition: {}", min, max, time); // NOI18N
357                if (!variableLight.isTransitionAvailable()) {
358                    time = 0.0d;
359                }
360
361                variableLight.setMinIntensity(min);
362                variableLight.setMaxIntensity(max);
363                variableLight.setTransitionTime(time);
364                updateLightStatusFields(false);
365            }
366        } catch (NumberFormatException ex) {
367            log.error("{}{}", Bundle.getMessage("ErrorTitle"), ex.toString());
368            nowStateTextField.setText(Bundle.getMessage("ErrorTitle"));
369        }
370    }
371
372    private void resetLightToCombo() {
373        if (light != null && light == to1.getSelectedItem()) {
374            return;
375        }
376        log.debug("Light changed in combobox to {}", to1.getSelectedItem());
377        // remove changelistener from previous Light
378        if (light != null) {
379            light.removePropertyChangeListener(_parentLightListener);
380        }
381        light = to1.getSelectedItem();
382        if (light != null) {
383            light.addPropertyChangeListener(
384                    _parentLightListener = new PropertyChangeListener() {
385                @Override
386                public void propertyChange(java.beans.PropertyChangeEvent e) {
387                    log.debug("recv propChange: {} {} -> {}", e.getPropertyName(), e.getOldValue(), e.getNewValue());
388                    updateLightStatusFields(false);
389                }
390            });
391            setControlFrameActive(true);
392            updateLightStatusFields(true);
393
394            StringBuilder name = new StringBuilder("<html>");
395            light.getLightControlList().forEach((otherLc) -> {
396                name.append(jmri.jmrit.beantable.LightTableAction.getDescriptionText(otherLc, otherLc.getControlType()));
397                name.append("<br>");
398            });
399
400            if (light.getLightControlList().isEmpty()) {
401                name.append("None");
402            }
403            name.append("</html>");
404            nowControllersTextField.setText(name.toString());
405
406            repaint();
407            revalidate();
408            pack();
409
410        } else {
411            setControlFrameActive(false);
412            nowStateTextField.setText(Bundle.getMessage("BeanStateUnknown"));
413            nowControllersTextField.setText("");
414        }
415    }
416
417    // if flag true, sets intensity and time fields
418    private void updateLightStatusFields(boolean flag) {
419        int knownState = light.getState();
420        switch (knownState) {
421            case Light.ON:
422                nowStateTextField.setText(Bundle.getMessage("StateOn"));
423                break;
424            case Light.INTERMEDIATE:
425                nowStateTextField.setText(Bundle.getMessage("LightStateIntermediate"));
426                break;
427            case Light.OFF:
428                nowStateTextField.setText(Bundle.getMessage("StateOff"));
429                break;
430            case Light.TRANSITIONINGTOFULLON:
431                nowStateTextField.setText(Bundle.getMessage("LightStateTransitioningToFullOn"));
432                break;
433            case Light.TRANSITIONINGHIGHER:
434                nowStateTextField.setText(Bundle.getMessage("LightStateTransitioningHigher"));
435                break;
436            case Light.TRANSITIONINGLOWER:
437                nowStateTextField.setText(Bundle.getMessage("LightStateTransitioningLower"));
438                break;
439            case Light.TRANSITIONINGTOFULLOFF:
440                nowStateTextField.setText(Bundle.getMessage("LightStateTransitioningToFullOff"));
441                break;
442            default:
443                nowStateTextField.setText(Bundle.getMessage("UnexpectedValueLabel", knownState));
444                break;
445        }
446        statusIsEnabledCheckBox.setSelected(light.getEnabled());
447        if (light instanceof VariableLight) {
448            VariableLight variableLight = (VariableLight)light;
449            statusIsVariableCheckBox.setSelected(true);
450            statusIsTransitionCheckBox.setSelected(variableLight.isTransitionAvailable());
451            nowIntensityLabel.setText(oneDigits.format(variableLight.getCurrentIntensity() * 100));
452            nowTransitionTimeLabel.setText(oneDotTwoDigits.format(variableLight.getTransitionTime()));
453            nowIntensityMinLabel.setText(oneDigits.format(variableLight.getMinIntensity() * 100));
454            nowIntensityMaxLabel.setText(oneDigits.format(variableLight.getMaxIntensity() * 100));
455            if (flag) {
456                intensityTextField.setText(oneDigits.format(variableLight.getTargetIntensity() * 100));
457                transitionTimeTextField.setText(oneDotTwoDigits.format(variableLight.getTransitionTime()));
458                intensityMinTextField.setText(oneDigits.format(variableLight.getMinIntensity() * 100));
459                intensityMaxTextField.setText(oneDigits.format(variableLight.getMaxIntensity() * 100));
460            }
461        } else {
462            statusIsVariableCheckBox.setSelected(false);
463            statusIsTransitionCheckBox.setSelected(false);
464            if (light.getState() == Light.ON) {
465                nowIntensityLabel.setText(oneDigits.format(1));
466            } else {
467                nowIntensityLabel.setText(oneDigits.format(0));
468            }
469            nowTransitionTimeLabel.setText(oneDotTwoDigits.format(0.0));
470            nowIntensityMinLabel.setText(oneDigits.format(0));
471            nowIntensityMaxLabel.setText(oneDigits.format(1));
472            if (flag) {
473                if (light.getState() == Light.ON) {
474                    nowIntensityLabel.setText(oneDigits.format(1));
475                } else {
476                    nowIntensityLabel.setText(oneDigits.format(0));
477                }
478                transitionTimeTextField.setText(oneDotTwoDigits.format(0.0));
479                intensityMinTextField.setText(oneDigits.format(0));
480                intensityMaxTextField.setText(oneDigits.format(1));
481            }
482        }
483    }
484
485    private final static Logger log = LoggerFactory.getLogger(SimpleLightCtrlFrame.class);
486
487}