001package jmri.jmrit.logixng.actions.swing;
002
003import java.awt.Color;
004import java.awt.event.ActionEvent;
005import java.util.List;
006
007import javax.annotation.CheckForNull;
008import javax.annotation.Nonnull;
009import javax.swing.*;
010
011import jmri.InstanceManager;
012import jmri.jmrit.logixng.*;
013import jmri.jmrit.logixng.actions.ActionTimer;
014import jmri.jmrit.logixng.util.TimerUnit;
015import jmri.util.swing.JComboBoxUtil;
016
017/**
018 * Configures an ActionTurnout object with a Swing JPanel.
019 *
020 * @author Daniel Bergqvist Copyright 2021
021 */
022public class ActionTimerSwing extends AbstractDigitalActionSwing {
023
024    public static final int MAX_NUM_TIMERS = 10;
025
026    private JCheckBox _startImmediately;
027    private JCheckBox _runContinuously;
028    private JCheckBox _startAndStopByStartExpression;
029    private JComboBox<TimerUnit> _unitComboBox;
030
031    private JCheckBox _delayByLocalVariables;
032    private JTextField _delayLocalVariablePrefix;
033
034    private JTextField _numTimers;
035    private JButton _addTimer;
036    private JButton _removeTimer;
037    private JTextField[] _timerSocketNames;
038    private JTextField[] _timerDelays;
039    private int numActions = 1;
040
041    private String getNewSocketName(ActionTimer action) {
042        int size = ActionTimer.NUM_STATIC_EXPRESSIONS + MAX_NUM_TIMERS;
043        String[] names = new String[size];
044        names[ActionTimer.EXPRESSION_START] = action.getStartExpressionSocket().getName();
045        names[ActionTimer.EXPRESSION_STOP] = action.getStopExpressionSocket().getName();
046        for (int i=0; i < MAX_NUM_TIMERS; i++) {
047            names[ActionTimer.NUM_STATIC_EXPRESSIONS + i] = _timerSocketNames[i].getText();
048        }
049        return action.getNewSocketName(names);
050    }
051
052    @Override
053    protected void createPanel(@CheckForNull Base object, @Nonnull JPanel buttonPanel) {
054        if ((object != null) && !(object instanceof ActionTimer)) {
055            throw new IllegalArgumentException("object must be an ActionTimer but is a: "+object.getClass().getName());
056        }
057
058        // Create a temporary action in case we don't have one.
059        ActionTimer action = object != null ? (ActionTimer)object : new ActionTimer("IQDA1", null);
060
061        numActions = action.getNumActions();
062
063        panel = new JPanel();
064        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
065
066        JPanel optionsPanel = new JPanel();
067
068        JPanel leftOptionsPanel = new JPanel();
069        leftOptionsPanel.setLayout(new BoxLayout(leftOptionsPanel, BoxLayout.Y_AXIS));
070        _startImmediately = new JCheckBox(Bundle.getMessage("ActionTimerSwing_StartImmediately"));
071        _runContinuously = new JCheckBox(Bundle.getMessage("ActionTimerSwing_RunContinuously"));
072        _startAndStopByStartExpression = new JCheckBox(Bundle.getMessage(
073                "ActionTimer_StartAndStopByStartExpression"));
074
075        _unitComboBox = new JComboBox<>();
076        for (TimerUnit u : TimerUnit.values()) _unitComboBox.addItem(u);
077        JComboBoxUtil.setupComboBoxMaxRows(_unitComboBox);
078        _unitComboBox.setSelectedItem(action.getUnit());
079
080        leftOptionsPanel.add(_startImmediately);
081        leftOptionsPanel.add(_runContinuously);
082        leftOptionsPanel.add(_startAndStopByStartExpression);
083
084        JPanel unitPanel = new JPanel();
085        unitPanel.add(new JLabel(Bundle.getMessage("ActionTimerSwing_Unit")));
086        unitPanel.add(_unitComboBox);
087        leftOptionsPanel.add(unitPanel);
088
089        leftOptionsPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
090
091
092        JPanel rightOptionsPanel = new JPanel();
093        rightOptionsPanel.setLayout(new BoxLayout(rightOptionsPanel, BoxLayout.Y_AXIS));
094        _delayByLocalVariables = new JCheckBox(Bundle.getMessage("ActionTimerSwing_DelayByLocalVariables"));
095        _delayByLocalVariables.setSelected(action.isDelayByLocalVariables());
096        _delayByLocalVariables.addActionListener((evt)->{updateEnableDisabled();});
097
098        _delayLocalVariablePrefix = new JTextField(20);
099        _delayLocalVariablePrefix.setText(action.getDelayLocalVariablePrefix());
100
101        rightOptionsPanel.add(_delayByLocalVariables);
102        rightOptionsPanel.add(new JLabel(Bundle.getMessage("ActionTimerSwing_DelayLocalVariablePrefix")));
103        rightOptionsPanel.add(_delayLocalVariablePrefix);
104
105
106        optionsPanel.add(leftOptionsPanel);
107        optionsPanel.add(rightOptionsPanel);
108
109
110        JPanel numActionsPanel = new JPanel();
111        _numTimers = new JTextField(Integer.toString(numActions));
112        _numTimers.setColumns(2);
113        _numTimers.setEnabled(false);
114
115        _addTimer = new JButton(Bundle.getMessage("ActionTimerSwing_AddTimer"));
116        _addTimer.addActionListener((ActionEvent e) -> {
117            numActions++;
118            _numTimers.setText(Integer.toString(numActions));
119            if (_timerSocketNames[numActions-1].getText().trim().isEmpty()) {
120                _timerSocketNames[numActions-1].setText(getNewSocketName(action));
121            }
122            _timerSocketNames[numActions-1].setEnabled(true);
123            _timerDelays[numActions-1].setEnabled(true);
124            if (numActions >= MAX_NUM_TIMERS) _addTimer.setEnabled(false);
125            _removeTimer.setEnabled(true);
126        });
127        if (numActions >= MAX_NUM_TIMERS) _addTimer.setEnabled(false);
128
129        _removeTimer = new JButton(Bundle.getMessage("ActionTimerSwing_RemoveTimer"));
130        _removeTimer.addActionListener((ActionEvent e) -> {
131            _timerSocketNames[numActions-1].setEnabled(false);
132            _timerDelays[numActions-1].setEnabled(false);
133            numActions--;
134            _numTimers.setText(Integer.toString(numActions));
135            _addTimer.setEnabled(true);
136            if ((numActions <= 1)
137                    || ((action.getNumActions() >= numActions)
138                        && (action.getActionSocket(numActions-1).isConnected()))) {
139                _removeTimer.setEnabled(false);
140            }
141        });
142        if ((numActions <= 1) || (action.getActionSocket(numActions-1).isConnected())) {
143            _removeTimer.setEnabled(false);
144        }
145
146        numActionsPanel.add(new JLabel(Bundle.getMessage("ActionTimerSwing_NumTimers")));
147        numActionsPanel.add(_numTimers);
148        numActionsPanel.add(_addTimer);
149        numActionsPanel.add(_removeTimer);
150
151        JPanel timerDelaysPanel = new JPanel();
152        timerDelaysPanel.setLayout(new BoxLayout(timerDelaysPanel, BoxLayout.Y_AXIS));
153        timerDelaysPanel.add(new JLabel(Bundle.getMessage("ActionTimerSwing_TimerDelays")));
154        JPanel timerDelaysSubPanel = new JPanel();
155        _timerSocketNames = new JTextField[MAX_NUM_TIMERS];
156        _timerDelays = new JTextField[MAX_NUM_TIMERS];
157
158        for (int i=0; i < MAX_NUM_TIMERS; i++) {
159            JPanel delayPanel = new JPanel();
160            delayPanel.setLayout(new BoxLayout(delayPanel, BoxLayout.Y_AXIS));
161            _timerDelays[i] = new JTextField("0");
162            _timerDelays[i].setColumns(7);
163            _timerDelays[i].setEnabled(false);
164            delayPanel.add(_timerDelays[i]);
165            _timerSocketNames[i] = new JTextField();
166            _timerSocketNames[i].setEnabled(false);
167            delayPanel.add(_timerSocketNames[i]);
168            timerDelaysSubPanel.add(delayPanel);
169            if (i < action.getNumActions()) {
170                String socketName = action.getActionSocket(i).getName();
171                _timerSocketNames[i].setText(socketName);
172                _timerSocketNames[i].setEnabled(true);
173                _timerDelays[i].setText(Integer.toString(action.getDelay(i)));
174                _timerDelays[i].setEnabled(true);
175            }
176        }
177        timerDelaysPanel.add(timerDelaysSubPanel);
178
179        panel.add(optionsPanel);
180        panel.add(numActionsPanel);
181        panel.add(timerDelaysPanel);
182
183        _startImmediately.setSelected(action.isStartImmediately());
184        _runContinuously.setSelected(action.isRunContinuously());
185        _startAndStopByStartExpression.setSelected(action.isStartAndStopByStartExpression());
186        _numTimers.setText(Integer.toString(action.getNumActions()));
187
188        updateEnableDisabled();
189    }
190
191    private void updateEnableDisabled() {
192        _delayLocalVariablePrefix.setEnabled(_delayByLocalVariables.isSelected());
193        for (int i=0; i < MAX_NUM_TIMERS; i++) {
194            _timerDelays[i].setEnabled(!_delayByLocalVariables.isSelected());
195        }
196    }
197
198    /** {@inheritDoc} */
199    @Override
200    public boolean validate(@Nonnull List<String> errorMessages) {
201        ActionTimer tempAction = new ActionTimer("IQDA1", null);
202
203        boolean hasErrors = false;
204        for (int i=0; i < numActions; i++) {
205            if (! tempAction.getActionSocket(0).validateName(_timerSocketNames[i].getText())) {
206                errorMessages.add(Bundle.getMessage("InvalidSocketName", _timerSocketNames[i].getText()));
207                hasErrors = true;
208            }
209        }
210        return !hasErrors;
211    }
212
213    /** {@inheritDoc} */
214    @Override
215    public MaleSocket createNewObject(@Nonnull String systemName, @CheckForNull String userName) {
216        ActionTimer action = new ActionTimer(systemName, userName);
217        updateObject(action);
218        return InstanceManager.getDefault(DigitalActionManager.class).registerAction(action);
219    }
220
221    /** {@inheritDoc} */
222    @Override
223    public void updateObject(@Nonnull Base object) {
224        if (!(object instanceof ActionTimer)) {
225            throw new IllegalArgumentException("object must be an ActionTimer but is a: "+object.getClass().getName());
226        }
227
228        ActionTimer action = (ActionTimer)object;
229
230        action.setStartImmediately(_startImmediately.isSelected());
231        action.setRunContinuously(_runContinuously.isSelected());
232        action.setStartAndStopByStartExpression(_startAndStopByStartExpression.isSelected());
233        action.setUnit(_unitComboBox.getItemAt(_unitComboBox.getSelectedIndex()));
234        action.setDelayByLocalVariables(_delayByLocalVariables.isSelected());
235        action.setDelayLocalVariablePrefix(_delayLocalVariablePrefix.getText());
236        action.setNumActions(numActions);
237
238        for (int i=0; i < numActions; i++) {
239            action.getActionSocket(i).setName(_timerSocketNames[i].getText());
240            action.setDelay(i, Integer.parseInt(_timerDelays[i].getText()));
241        }
242    }
243
244    /** {@inheritDoc} */
245    @Override
246    public String toString() {
247        return Bundle.getMessage("ActionTimer_Short");
248    }
249
250    @Override
251    public void dispose() {
252    }
253
254}