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}