001package jmri.jmrit.logixng.actions.swing; 002 003import java.awt.Color; 004import java.util.List; 005 006import javax.annotation.CheckForNull; 007import javax.annotation.Nonnull; 008import javax.swing.*; 009import javax.swing.event.ChangeEvent; 010 011import jmri.InstanceManager; 012import jmri.NamedBeanHandle; 013import jmri.NamedBeanHandleManager; 014import jmri.SignalHead; 015import jmri.SignalHeadManager; 016import jmri.jmrit.logixng.*; 017import jmri.jmrit.logixng.actions.ActionSignalHead; 018import jmri.jmrit.logixng.swing.SwingConfiguratorInterface; 019import jmri.jmrit.logixng.util.swing.LogixNG_SelectNamedBeanSwing; 020import jmri.jmrit.logixng.util.parser.ParserException; 021import jmri.util.swing.BeanSelectPanel; 022import jmri.util.swing.JComboBoxUtil; 023 024/** 025 * Configures an ActionSignalHead object with a Swing JPanel. 026 * 027 * @author Daniel Bergqvist Copyright 2021 028 */ 029public class ActionSignalHeadSwing extends AbstractDigitalActionSwing { 030 031 public static final int NUM_COLUMNS_TEXT_FIELDS = 20; 032 033 private LogixNG_SelectNamedBeanSwing<SignalHead> _selectNamedBeanSwing; 034 035 private JTabbedPane _tabbedPaneOperationType; 036 private JPanel _panelOperationTypeDirect; 037 private JPanel _panelOperationTypeReference; 038 private JPanel _panelOperationTypeLocalVariable; 039 private JPanel _panelOperationTypeFormula; 040 041 private JComboBox<ActionSignalHead.OperationType> _operationComboBox; 042 private JTextField _signalHeadOperationReferenceTextField; 043 private JTextField _signalHeadOperationLocalVariableTextField; 044 private JTextField _signalHeadOperationFormulaTextField; 045 046 private JTabbedPane _tabbedPaneAppearanceType; 047 private JPanel _panelAppearanceTypeDirect; 048 private JPanel _panelAppearanceTypeReference; 049 private JPanel _panelAppearanceTypeLocalVariable; 050 private JPanel _panelAppearanceTypeFormula; 051 052 private JComboBox<SignalHeadAppearance> _signalHeadAppearanceComboBox; 053 private JTextField _signalHeadAppearanceReferenceTextField; 054 private JTextField _signalHeadAppearanceLocalVariableTextField; 055 private JTextField _signalHeadAppearanceFormulaTextField; 056 057 private BeanSelectPanel<SignalHead> _exampleSignalHeadBeanPanel; 058 059 060 public ActionSignalHeadSwing() { 061 } 062 063 public ActionSignalHeadSwing(JDialog dialog) { 064 super.setJDialog(dialog); 065 } 066 067 @Override 068 protected void createPanel(@CheckForNull Base object, @Nonnull JPanel buttonPanel) { 069 ActionSignalHead action = (ActionSignalHead)object; 070 071 _selectNamedBeanSwing = new LogixNG_SelectNamedBeanSwing<>( 072 InstanceManager.getDefault(SignalHeadManager.class), getJDialog(), this); 073 074 panel = new JPanel(); 075 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 076 077 JPanel _tabbedPaneNamedBean; 078 079 if (action != null) { 080 _tabbedPaneNamedBean = _selectNamedBeanSwing.createPanel(action.getSelectNamedBean()); 081 } else { 082 _tabbedPaneNamedBean = _selectNamedBeanSwing.createPanel(null); 083 } 084 085 JPanel examplePanel = new JPanel(); 086 JPanel innerExamplePanel = new JPanel(); 087 innerExamplePanel.setBorder(BorderFactory.createLineBorder(Color.black)); 088 _exampleSignalHeadBeanPanel = new BeanSelectPanel<>(InstanceManager.getDefault(SignalHeadManager.class), null); 089 innerExamplePanel.add(_exampleSignalHeadBeanPanel); 090 091 _exampleSignalHeadBeanPanel.getBeanCombo().addActionListener((java.awt.event.ActionEvent e) -> { 092 setAppearanceComboBox(null); 093 }); 094 095 096 JPanel actionPanel = new JPanel(); 097 098 _selectNamedBeanSwing.addAddressingListener((ChangeEvent e) -> { 099 setGuiEnabledStates(); 100 }); 101 102 _selectNamedBeanSwing.getBeanSelectPanel().getBeanCombo() 103 .addActionListener((java.awt.event.ActionEvent e) -> { 104 setAppearanceComboBox(null); 105 }); 106 107 // Set up the tabbed pane for selecting the operation 108 _tabbedPaneOperationType = new JTabbedPane(); 109 _panelOperationTypeDirect = new javax.swing.JPanel(); 110 _panelOperationTypeDirect.setLayout(new BoxLayout(_panelOperationTypeDirect, BoxLayout.Y_AXIS)); 111 _panelOperationTypeReference = new javax.swing.JPanel(); 112 _panelOperationTypeReference.setLayout(new BoxLayout(_panelOperationTypeReference, BoxLayout.Y_AXIS)); 113 _panelOperationTypeLocalVariable = new javax.swing.JPanel(); 114 _panelOperationTypeLocalVariable.setLayout(new BoxLayout(_panelOperationTypeLocalVariable, BoxLayout.Y_AXIS)); 115 _panelOperationTypeFormula = new javax.swing.JPanel(); 116 _panelOperationTypeFormula.setLayout(new BoxLayout(_panelOperationTypeFormula, BoxLayout.Y_AXIS)); 117 118 _tabbedPaneOperationType.addTab(NamedBeanAddressing.Direct.toString(), _panelOperationTypeDirect); 119 _tabbedPaneOperationType.addTab(NamedBeanAddressing.Reference.toString(), _panelOperationTypeReference); 120 _tabbedPaneOperationType.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelOperationTypeLocalVariable); 121 _tabbedPaneOperationType.addTab(NamedBeanAddressing.Formula.toString(), _panelOperationTypeFormula); 122 123 _tabbedPaneOperationType.addChangeListener((ChangeEvent e) -> { 124 setGuiEnabledStates(); 125 }); 126 127 _operationComboBox = new JComboBox<>(); 128 for (ActionSignalHead.OperationType e : ActionSignalHead.OperationType.values()) { 129 _operationComboBox.addItem(e); 130 } 131 JComboBoxUtil.setupComboBoxMaxRows(_operationComboBox); 132 _operationComboBox.addActionListener(e -> { 133 setGuiEnabledStates(); 134 }); 135 136 _panelOperationTypeDirect.add(new JLabel(Bundle.getMessage("ActionSignalHead_Operation"))); 137 _panelOperationTypeDirect.add(_operationComboBox); 138 139 _signalHeadOperationReferenceTextField = new JTextField(); 140 _signalHeadOperationReferenceTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS); 141 _panelOperationTypeReference.add(new JLabel(Bundle.getMessage("ActionSignalHead_Operation"))); 142 _panelOperationTypeReference.add(_signalHeadOperationReferenceTextField); 143 144 _signalHeadOperationLocalVariableTextField = new JTextField(); 145 _signalHeadOperationLocalVariableTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS); 146 _panelOperationTypeLocalVariable.add(new JLabel(Bundle.getMessage("ActionSignalHead_Operation"))); 147 _panelOperationTypeLocalVariable.add(_signalHeadOperationLocalVariableTextField); 148 149 _signalHeadOperationFormulaTextField = new JTextField(); 150 _signalHeadOperationFormulaTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS); 151 _panelOperationTypeFormula.add(new JLabel(Bundle.getMessage("ActionSignalHead_Operation"))); 152 _panelOperationTypeFormula.add(_signalHeadOperationFormulaTextField); 153 154 155 // Set up the tabbed pane for selecting the appearance 156 _tabbedPaneAppearanceType = new JTabbedPane(); 157 _panelAppearanceTypeDirect = new javax.swing.JPanel(); 158 _panelAppearanceTypeDirect.setLayout(new BoxLayout(_panelAppearanceTypeDirect, BoxLayout.Y_AXIS)); 159 _panelAppearanceTypeReference = new javax.swing.JPanel(); 160 _panelAppearanceTypeReference.setLayout(new BoxLayout(_panelAppearanceTypeReference, BoxLayout.Y_AXIS)); 161 _panelAppearanceTypeLocalVariable = new javax.swing.JPanel(); 162 _panelAppearanceTypeLocalVariable.setLayout(new BoxLayout(_panelAppearanceTypeLocalVariable, BoxLayout.Y_AXIS)); 163 _panelAppearanceTypeFormula = new javax.swing.JPanel(); 164 _panelAppearanceTypeFormula.setLayout(new BoxLayout(_panelAppearanceTypeFormula, BoxLayout.Y_AXIS)); 165 166 _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.Direct.toString(), _panelAppearanceTypeDirect); 167 _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.Reference.toString(), _panelAppearanceTypeReference); 168 _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelAppearanceTypeLocalVariable); 169 _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.Formula.toString(), _panelAppearanceTypeFormula); 170 171 _tabbedPaneAppearanceType.addChangeListener((ChangeEvent e) -> { 172 setGuiEnabledStates(); 173 }); 174 175 _signalHeadAppearanceComboBox = new JComboBox<>(); 176 _panelAppearanceTypeDirect.add(new JLabel(Bundle.getMessage("ActionSignalHead_Appearance"))); 177 _panelAppearanceTypeDirect.add(_signalHeadAppearanceComboBox); 178 179 _signalHeadAppearanceReferenceTextField = new JTextField(); 180 _signalHeadAppearanceReferenceTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS); 181 _panelAppearanceTypeReference.add(new JLabel(Bundle.getMessage("ActionSignalHead_Appearance"))); 182 _panelAppearanceTypeReference.add(_signalHeadAppearanceReferenceTextField); 183 184 _signalHeadAppearanceLocalVariableTextField = new JTextField(); 185 _signalHeadAppearanceLocalVariableTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS); 186 _panelAppearanceTypeLocalVariable.add(new JLabel(Bundle.getMessage("ActionSignalHead_Appearance"))); 187 _panelAppearanceTypeLocalVariable.add(_signalHeadAppearanceLocalVariableTextField); 188 189 _signalHeadAppearanceFormulaTextField = new JTextField(); 190 _signalHeadAppearanceFormulaTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS); 191 _panelAppearanceTypeFormula.add(new JLabel(Bundle.getMessage("ActionSignalHead_Appearance"))); 192 _panelAppearanceTypeFormula.add(_signalHeadAppearanceFormulaTextField); 193 194 195 JPanel notePanel = new JPanel(); 196 notePanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.white)); 197 examplePanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.white)); 198 199 JLabel noteLabel = new JLabel(Bundle.getMessage("SignalExampleText", 200 Bundle.getMessage("SignalExampleHead"), 201 Bundle.getMessage("SignalExampleAppearances"))); 202 notePanel.add(noteLabel); 203 204 205 examplePanel.add(new JLabel(Bundle.getMessage("ActionSignalHead_ExampleBean"))); 206 examplePanel.add(innerExamplePanel); 207 208 209 if (action != null) { 210 if (action.getSelectExampleNamedBean().getNamedBean() != null) { 211 _exampleSignalHeadBeanPanel.setDefaultNamedBean(action.getSelectExampleNamedBean().getNamedBean().getBean()); 212 } 213 214 switch (action.getOperationAddressing()) { 215 case Direct: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeDirect); break; 216 case Reference: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeReference); break; 217 case LocalVariable: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeLocalVariable); break; 218 case Formula: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeFormula); break; 219 default: throw new IllegalArgumentException("invalid _addressing state: " + action.getOperationAddressing().name()); 220 } 221 _operationComboBox.setSelectedItem(action.getOperationType()); 222 _signalHeadOperationReferenceTextField.setText(action.getOperationReference()); 223 _signalHeadOperationLocalVariableTextField.setText(action.getOperationLocalVariable()); 224 _signalHeadOperationFormulaTextField.setText(action.getOperationFormula()); 225 226 227 switch (action.getAppearanceAddressing()) { 228 case Direct: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeDirect); break; 229 case Reference: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeReference); break; 230 case LocalVariable: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeLocalVariable); break; 231 case Formula: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeFormula); break; 232 default: throw new IllegalArgumentException("invalid _addressing state: " + action.getAppearanceAddressing().name()); 233 } 234 _signalHeadAppearanceReferenceTextField.setText(action.getAppearanceReference()); 235 _signalHeadAppearanceLocalVariableTextField.setText(action.getAppearanceLocalVariable()); 236 _signalHeadAppearanceFormulaTextField.setText(action.getAppearanceFormula()); 237 238 jmri.util.ThreadingUtil.runOnGUIEventually(() -> { setAppearanceComboBox(action); }); 239 } 240 241 JComponent[] components = new JComponent[]{ 242 _tabbedPaneNamedBean, 243 _tabbedPaneOperationType, 244 _tabbedPaneAppearanceType 245 }; 246 247 List<JComponent> componentList = SwingConfiguratorInterface.parseMessage( 248 Bundle.getMessage("ActionSignalHead_Components"), components); 249 250 for (JComponent c : componentList) actionPanel.add(c); 251 252 panel.add(actionPanel); 253 panel.add(notePanel); 254 panel.add(examplePanel); 255 256 setGuiEnabledStates(); 257 } 258 259 260 private void setGuiEnabledStates() { 261 _tabbedPaneAppearanceType.setEnabled(false); 262 _signalHeadAppearanceComboBox.setEnabled(false); 263 _signalHeadAppearanceReferenceTextField.setEnabled(false); 264 _signalHeadAppearanceLocalVariableTextField.setEnabled(false); 265 _signalHeadAppearanceFormulaTextField.setEnabled(false); 266 _exampleSignalHeadBeanPanel.getBeanCombo().setEnabled(false); 267 268 if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeDirect && 269 _operationComboBox.getSelectedItem() != ActionSignalHead.OperationType.Appearance) { 270 return; 271 } 272 273 _tabbedPaneAppearanceType.setEnabled(true); 274 275 if (_selectNamedBeanSwing.getAddressing() != NamedBeanAddressing.Direct && 276 _tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect) { 277 _exampleSignalHeadBeanPanel.getBeanCombo().setEnabled(true); 278 } 279 280 if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect) { 281 _signalHeadAppearanceComboBox.setEnabled(true); 282 } 283 if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeReference) { 284 _signalHeadAppearanceReferenceTextField.setEnabled(true); 285 } 286 if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeLocalVariable) { 287 _signalHeadAppearanceLocalVariableTextField.setEnabled(true); 288 } 289 if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeFormula) { 290 _signalHeadAppearanceFormulaTextField.setEnabled(true); 291 } 292 } 293 294 private void setAppearanceComboBox(ActionSignalHead action) { 295 SignalHead sh; 296 if (_selectNamedBeanSwing.getAddressing() == NamedBeanAddressing.Direct) { 297 sh = _selectNamedBeanSwing.getBean(); 298 } else { 299 sh = _exampleSignalHeadBeanPanel.getBeanCombo().getSelectedItem(); 300 } 301 302 if (sh != null) { 303 _signalHeadAppearanceComboBox.removeAllItems(); 304 int[] states = sh.getValidStates(); 305 for (int s : states) { 306 SignalHeadAppearance sha = new SignalHeadAppearance(); 307 sha._state = s; 308 sha._name = sh.getAppearanceName(s); 309 _signalHeadAppearanceComboBox.addItem(sha); 310 if (action != null) { 311 if (action.getAppearance() == s) _signalHeadAppearanceComboBox.setSelectedItem(sha); 312 } 313 } 314 JComboBoxUtil.setupComboBoxMaxRows(_signalHeadAppearanceComboBox); 315 } 316 } 317 318 319 /** {@inheritDoc} */ 320 @Override 321 public boolean validate(@Nonnull List<String> errorMessages) { 322 // Create a temporary action to test formula 323 ActionSignalHead action = new ActionSignalHead("IQDA1", null); 324 325 _selectNamedBeanSwing.validate(action.getSelectNamedBean(), errorMessages); 326 327 try { 328 if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeReference) { 329 action.setOperationReference(_signalHeadOperationReferenceTextField.getText()); 330 } 331 } catch (IllegalArgumentException e) { 332 errorMessages.add(e.getMessage()); 333 return false; 334 } 335 336 return errorMessages.isEmpty(); 337 } 338 339 /** {@inheritDoc} */ 340 @Override 341 public MaleSocket createNewObject(@Nonnull String systemName, @CheckForNull String userName) { 342 ActionSignalHead action = new ActionSignalHead(systemName, userName); 343 updateObject(action); 344 return InstanceManager.getDefault(DigitalActionManager.class).registerAction(action); 345 } 346 347 /** {@inheritDoc} */ 348 @Override 349 public void updateObject(@Nonnull Base object) { 350 if (! (object instanceof ActionSignalHead)) { 351 throw new IllegalArgumentException("object must be an ActionSignalHead but is a: "+object.getClass().getName()); 352 } 353 ActionSignalHead action = (ActionSignalHead)object; 354 355 _selectNamedBeanSwing.updateObject(action.getSelectNamedBean()); 356 357 if (!_exampleSignalHeadBeanPanel.isEmpty() 358 && (_selectNamedBeanSwing.getAddressing() != NamedBeanAddressing.Direct) 359 && (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect)) { 360 361 SignalHead signalHead = _exampleSignalHeadBeanPanel.getNamedBean(); 362 if (signalHead != null) { 363 NamedBeanHandle<SignalHead> handle 364 = InstanceManager.getDefault(NamedBeanHandleManager.class) 365 .getNamedBeanHandle(signalHead.getDisplayName(), signalHead); 366 action.getSelectExampleNamedBean().setNamedBean(handle); 367 } 368 } else { 369 action.getSelectExampleNamedBean().removeNamedBean(); 370 } 371 372 try { 373 if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeDirect) { 374 action.setOperationAddressing(NamedBeanAddressing.Direct); 375 action.setOperationType((ActionSignalHead.OperationType)_operationComboBox.getSelectedItem()); 376 } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeReference) { 377 action.setOperationAddressing(NamedBeanAddressing.Reference); 378 action.setOperationReference(_signalHeadOperationReferenceTextField.getText()); 379 } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeLocalVariable) { 380 action.setOperationAddressing(NamedBeanAddressing.LocalVariable); 381 action.setOperationLocalVariable(_signalHeadOperationLocalVariableTextField.getText()); 382 } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeFormula) { 383 action.setOperationAddressing(NamedBeanAddressing.Formula); 384 action.setOperationFormula(_signalHeadOperationFormulaTextField.getText()); 385 } else { 386 throw new IllegalArgumentException("_tabbedPaneOperationType has unknown selection"); 387 } 388 389 if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect) { 390 action.setAppearanceAddressing(NamedBeanAddressing.Direct); 391 392 if (_signalHeadAppearanceComboBox.getItemCount() > 0) { 393 action.setAppearance(_signalHeadAppearanceComboBox 394 .getItemAt(_signalHeadAppearanceComboBox.getSelectedIndex())._state); 395 } 396 } else if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeReference) { 397 action.setAppearanceAddressing(NamedBeanAddressing.Reference); 398 action.setAppearanceReference(_signalHeadAppearanceReferenceTextField.getText()); 399 } else if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeLocalVariable) { 400 action.setAppearanceAddressing(NamedBeanAddressing.LocalVariable); 401 action.setAppearanceLocalVariable(_signalHeadAppearanceLocalVariableTextField.getText()); 402 } else if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeFormula) { 403 action.setAppearanceAddressing(NamedBeanAddressing.Formula); 404 action.setAppearanceFormula(_signalHeadAppearanceFormulaTextField.getText()); 405 } else { 406 throw new IllegalArgumentException("_tabbedPaneAppearanceType has unknown selection"); 407 } 408 } catch (ParserException e) { 409 throw new RuntimeException("ParserException: "+e.getMessage(), e); 410 } 411 } 412 413 /** {@inheritDoc} */ 414 @Override 415 public String toString() { 416 return Bundle.getMessage("SignalHead_Short"); 417 } 418 419 @Override 420 public void dispose() { 421 } 422 423 424 private static class SignalHeadAppearance { 425 426 private int _state; 427 private String _name; 428 429 @Override 430 public String toString() { 431 return _name; 432 } 433 434 } 435 436// private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionSignalHeadSwing.class); 437 438}