001package jmri.jmrix; 002 003import java.awt.Color; 004import java.awt.GridBagConstraints; 005import java.awt.Insets; 006import java.awt.event.ActionEvent; 007import java.awt.event.KeyEvent; 008import java.awt.event.KeyListener; 009import javax.swing.JButton; 010import java.util.Map; 011import javax.swing.JCheckBox; 012import javax.swing.JComboBox; 013import javax.swing.JComponent; 014import javax.swing.JLabel; 015import javax.swing.JPanel; 016import javax.swing.JPasswordField; 017import javax.swing.JSpinner; 018import javax.swing.JTextField; 019import javax.swing.SpinnerNumberModel; 020import jmri.InstanceManager; 021import jmri.UserPreferencesManager; 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025/** 026 * Abstract base class for common implementation of the NetworkConnectionConfig. 027 * 028 * @author Bob Jacobsen Copyright (C) 2001, 2003 029 */ 030abstract public class AbstractNetworkConnectionConfig extends AbstractConnectionConfig { 031 032 /** 033 * Create a connection configuration with a preexisting adapter. This is 034 * used principally when loading a configuration that defines this 035 * connection. 036 * 037 * @param p the adapter to create a connection configuration for 038 */ 039 public AbstractNetworkConnectionConfig(NetworkPortAdapter p) { 040 adapter = p; 041 } 042 043 /** 044 * Ctor for a functional object with no preexisting adapter. Expect that the 045 * subclass setInstance() will fill the adapter member. 046 */ 047 public AbstractNetworkConnectionConfig() { 048 } 049 050 protected boolean init = false; 051 052 /** 053 * {@inheritDoc} 054 */ 055 @Override 056 protected void checkInitDone() { 057 log.debug("init called for {}", name()); 058 if (init) { 059 return; 060 } 061 hostNameField.addActionListener(e -> { 062 adapter.setHostName(hostNameField.getText()); 063 p.setComboBoxLastSelection(adapter.getClass().getName() + ".hostname", hostNameField.getText()); 064 }); 065 hostNameField.addKeyListener(new KeyListener() { 066 @Override 067 public void keyPressed(KeyEvent keyEvent) { 068 } 069 070 @Override 071 public void keyReleased(KeyEvent keyEvent) { 072 adapter.setHostName(hostNameField.getText()); 073 p.setComboBoxLastSelection(adapter.getClass().getName() + ".hostname", hostNameField.getText()); 074 } 075 076 @Override 077 public void keyTyped(KeyEvent keyEvent) { 078 } 079 }); 080 portField.addActionListener(e -> { 081 try { 082 adapter.setPort(Integer.parseInt(portField.getText())); 083 } catch (NumberFormatException ex) { 084 log.warn("Could not parse port attribute"); 085 } 086 }); 087 088 portField.addKeyListener(new KeyListener() { 089 @Override 090 public void keyPressed(KeyEvent keyEvent) { 091 } 092 093 @Override 094 public void keyReleased(KeyEvent keyEvent) { 095 try { 096 adapter.setPort(Integer.parseInt(portField.getText())); 097 } catch (java.lang.NumberFormatException ex) { 098 log.warn("Could not parse port attribute"); 099 } 100 } 101 102 @Override 103 public void keyTyped(KeyEvent keyEvent) { 104 } 105 }); 106 107 adNameField.addActionListener(e -> adapter.setAdvertisementName(adNameField.getText())); 108 109 adNameField.addKeyListener(new KeyListener() { 110 @Override 111 public void keyPressed(KeyEvent keyEvent) { 112 } 113 114 @Override 115 public void keyReleased(KeyEvent keyEvent) { 116 adapter.setAdvertisementName(adNameField.getText()); 117 } 118 119 @Override 120 public void keyTyped(KeyEvent keyEvent) { 121 } 122 }); 123 124 serviceTypeField.addActionListener(e -> adapter.setServiceType(serviceTypeField.getText())); 125 126 serviceTypeField.addKeyListener(new KeyListener() { 127 @Override 128 public void keyPressed(KeyEvent keyEvent) { 129 } 130 131 @Override 132 public void keyReleased(KeyEvent keyEvent) { 133 adapter.setServiceType(serviceTypeField.getText()); 134 } 135 136 @Override 137 public void keyTyped(KeyEvent keyEvent) { 138 } 139 }); 140 141 options.entrySet().forEach(entry -> { 142 final String item = entry.getKey(); 143 if (entry.getValue().getComponent() instanceof JComboBox) { 144 ((JComboBox<?>) entry.getValue().getComponent()).addActionListener((ActionEvent e) -> { 145 log.debug("option combo box changed to {}", options.get(item).getItem()); 146 adapter.setOptionState(item, options.get(item).getItem()); 147 }); 148 } else if (entry.getValue().getComponent() instanceof JTextField) { 149 // listen for enter 150 ((JTextField) entry.getValue().getComponent()).addActionListener((ActionEvent e) -> { 151 log.debug("option text field changed to {}", options.get(item).getItem()); 152 adapter.setOptionState(item, options.get(item).getItem()); 153 }); 154 // listen for key press so you don't have to hit enter 155 (entry.getValue().getComponent()).addKeyListener(new KeyListener() { 156 @Override 157 public void keyPressed(KeyEvent keyEvent) { 158 } 159 160 @Override 161 public void keyReleased(KeyEvent keyEvent) { 162 adapter.setOptionState(item, options.get(item).getItem()); 163 } 164 165 @Override 166 public void keyTyped(KeyEvent keyEvent) { 167 } 168 }); 169 } 170 }); 171 172 addNameEntryCheckers(adapter); 173 174 // set/change delay interval between (actually before) output (Turnout) commands 175 outputIntervalSpinner.addChangeListener(e -> adapter.getSystemConnectionMemo().setOutputInterval((Integer) outputIntervalSpinner.getValue())); 176 177 init = true; 178 } 179 180 @Override 181 public void updateAdapter() { 182 if (adapter.getMdnsConfigure()) { 183 // set the hostname if it is not blank 184 if (!(hostNameField.getText().isEmpty())) { 185 adapter.setHostName(hostNameField.getText()); 186 } 187 // set the advertisement name if it is not blank 188 if (!(adNameField.getText().isEmpty())) { 189 adapter.setAdvertisementName(adNameField.getText()); 190 } 191 // set the Service Type if it is not blank. 192 if (!(serviceTypeField.getText().isEmpty())) { 193 adapter.setServiceType(serviceTypeField.getText()); 194 } 195 // and get the host IP and port number 196 // via mdns 197 adapter.autoConfigure(); 198 } else { 199 adapter.setHostName(hostNameField.getText()); 200 adapter.setPort(Integer.parseInt(portField.getText())); 201 } 202 options.entrySet().forEach(entry -> { 203 adapter.setOptionState(entry.getKey(), entry.getValue().getItem()); 204 }); 205 if (adapter.getSystemConnectionMemo() != null && !adapter.getSystemConnectionMemo().setSystemPrefix(systemPrefixField.getText())) { 206 systemPrefixField.setText(adapter.getSystemConnectionMemo().getSystemPrefix()); 207 connectionNameField.setText(adapter.getSystemConnectionMemo().getUserName()); 208 } 209 } 210 211 UserPreferencesManager p = InstanceManager.getDefault(UserPreferencesManager.class); 212 protected JTextField hostNameField = new JTextField(15); 213 protected JLabel hostNameFieldLabel; 214 protected JTextField portField = new JTextField(10); 215 protected JLabel portFieldLabel; 216 217 protected JCheckBox showAutoConfig = new JCheckBox(Bundle.getMessage("AutoConfigLabel")); 218 protected JTextField adNameField = new JTextField(15); 219 protected JLabel adNameFieldLabel; 220 protected JTextField serviceTypeField = new JTextField(15); 221 protected JLabel serviceTypeFieldLabel; 222 223 protected SpinnerNumberModel intervalSpinner = new SpinnerNumberModel(250, 0, 10000, 1); // 10 sec max seems long enough 224 protected JSpinner outputIntervalSpinner = new JSpinner(intervalSpinner); 225 protected JLabel outputIntervalLabel; 226 protected JButton outputIntervalReset = new JButton(Bundle.getMessage("ButtonReset")); 227 228 protected NetworkPortAdapter adapter = null; 229 230 @Override 231 public NetworkPortAdapter getAdapter() { 232 return adapter; 233 } 234 235 /** 236 * {@inheritDoc} 237 */ 238 @Override 239 abstract protected void setInstance(); 240 241 @Override 242 public String getInfo() { 243 return adapter.getCurrentPortName(); 244 } 245 246 protected void checkOptionValueValidity(String i, JComboBox<String> opt) { 247 if (!adapter.getOptionState(i).equals(opt.getSelectedItem())) { 248 // no, set 1st option choice 249 opt.setSelectedIndex(0); 250 // log before setting new value to show old value 251 log.warn("Loading found invalid value for option {}, found \"{}\", setting to \"{}\"", i, adapter.getOptionState(i), opt.getSelectedItem()); 252 adapter.setOptionState(i, (String) opt.getSelectedItem()); 253 } 254 } 255 256 /** 257 * {@inheritDoc} 258 */ 259 @Override 260 public void loadDetails(final JPanel details) { 261 _details = details; 262 setInstance(); 263 if (!init) { 264 //Build up list of options 265 //Hashtable<String, AbstractPortController.Option> adapterOptions = ((AbstractPortController)adapter).getOptionList(); 266 String[] optionsAvailable = adapter.getOptions(); 267 options.clear(); 268 for (String i : optionsAvailable) { 269 if (adapter.isOptionTypeText(i) ) { 270 JTextField opt = new JTextField(15); 271 opt.setText(adapter.getOptionState(i)); 272 options.put(i, new Option(adapter.getOptionDisplayName(i), opt, adapter.isOptionAdvanced(i))); 273 } else if (adapter.isOptionTypePassword(i) ) { 274 JTextField opt = new JPasswordField(15); 275 opt.setText(adapter.getOptionState(i)); 276 options.put(i, new Option(adapter.getOptionDisplayName(i), opt, adapter.isOptionAdvanced(i))); 277 } else { 278 JComboBox<String> opt = new JComboBox<>(adapter.getOptionChoices(i)); 279 opt.setSelectedItem(adapter.getOptionState(i)); 280 281 // check that it worked 282 checkOptionValueValidity(i, opt); 283 284 options.put(i, new Option(adapter.getOptionDisplayName(i), opt, adapter.isOptionAdvanced(i))); 285 } 286 } 287 } 288 289 if (hostNameField.getActionListeners().length > 0) { 290 hostNameField.removeActionListener(hostNameField.getActionListeners()[0]); 291 } 292 293 if (adapter.getSystemConnectionMemo() != null) { 294 systemPrefixField.setText(adapter.getSystemConnectionMemo().getSystemPrefix()); 295 connectionNameField.setText(adapter.getSystemConnectionMemo().getUserName()); 296 NUMOPTIONS = NUMOPTIONS + 2; 297 } 298 NUMOPTIONS = NUMOPTIONS + options.size(); 299 300 hostNameField.setText(adapter.getHostName()); 301 hostNameFieldLabel = new JLabel(Bundle.getMessage("HostFieldLabel")); 302 hostNameField.setToolTipText(Bundle.getMessage("HostFieldToolTip")); 303 if (adapter.getHostName() == null || adapter.getHostName().isEmpty()) { 304 hostNameField.setText(p.getComboBoxLastSelection(adapter.getClass().getName() + ".hostname")); 305 adapter.setHostName(hostNameField.getText()); 306 } 307 308 portField.setToolTipText(Bundle.getMessage("PortFieldToolTip")); 309 portField.setEnabled(true); 310 portField.setText("" + adapter.getPort()); 311 portFieldLabel = new JLabel(Bundle.getMessage("PortFieldLabel")); 312 313 adNameField.setToolTipText(Bundle.getMessage("AdNameFieldToolTip")); 314 adNameField.setEnabled(false); 315 adNameField.setText("" + adapter.getAdvertisementName()); 316 adNameFieldLabel = new JLabel(Bundle.getMessage("AdNameFieldLabel")); 317 adNameFieldLabel.setEnabled(false); 318 319 serviceTypeField.setToolTipText(Bundle.getMessage("ServiceTypeFieldToolTip")); 320 serviceTypeField.setEnabled(false); 321 serviceTypeField.setText("" + adapter.getServiceType()); 322 serviceTypeFieldLabel = new JLabel(Bundle.getMessage("ServiceTypeFieldLabel")); 323 serviceTypeFieldLabel.setEnabled(false); 324 325 // connection (memo) specific output command delay option, calls jmri.jmrix.SystemConnectionMemo#setOutputInterval(int) 326 outputIntervalLabel = new JLabel(Bundle.getMessage("OutputIntervalLabel")); 327 outputIntervalSpinner.setToolTipText(Bundle.getMessage("OutputIntervalTooltip", 328 adapter.getSystemConnectionMemo().getDefaultOutputInterval(),adapter.getManufacturer())); 329 JTextField field = ((JSpinner.DefaultEditor) outputIntervalSpinner.getEditor()).getTextField(); 330 field.setColumns(6); 331 outputIntervalSpinner.setMaximumSize(outputIntervalSpinner.getPreferredSize()); // set spinner JTextField width 332 outputIntervalSpinner.setValue(adapter.getSystemConnectionMemo().getOutputInterval()); 333 outputIntervalSpinner.setEnabled(true); 334 outputIntervalReset.addActionListener((ActionEvent event) -> { 335 outputIntervalSpinner.setValue(adapter.getSystemConnectionMemo().getDefaultOutputInterval()); 336 adapter.getSystemConnectionMemo().setOutputInterval(adapter.getSystemConnectionMemo().getDefaultOutputInterval()); 337 }); 338 339 showAutoConfig.setFont(showAutoConfig.getFont().deriveFont(9f)); 340 showAutoConfig.setForeground(Color.blue); 341 showAutoConfig.addItemListener(e -> setAutoNetworkConfig()); 342 showAutoConfig.setSelected(adapter.getMdnsConfigure()); 343 setAutoNetworkConfig(); 344 345 showAdvanced.setFont(showAdvanced.getFont().deriveFont(9f)); 346 showAdvanced.setForeground(Color.blue); 347 showAdvanced.addItemListener(e -> showAdvancedItems()); 348 showAdvancedItems(); 349 350 init = false; // need to reload action listeners 351 checkInitDone(); 352 } 353 354 @Override 355 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", 356 justification = "type was checked before casting") 357 protected void showAdvancedItems() { 358 _details.removeAll(); 359 cL.anchor = GridBagConstraints.WEST; 360 cL.insets = new Insets(2, 5, 0, 5); 361 cR.insets = new Insets(2, 0, 0, 5); 362 cR.anchor = GridBagConstraints.WEST; 363 cR.gridx = 1; 364 cL.gridx = 0; 365 int i = 0; 366 int stdrows = 0; 367 boolean incAdvancedOptions = true; 368 if (!isPortAdvanced()) { 369 stdrows++; 370 } 371 if (!isHostNameAdvanced()) { 372 stdrows++; 373 } 374 for (Map.Entry<String, Option> entry : options.entrySet()) { 375 if (!entry.getValue().isAdvanced()) { 376 stdrows++; 377 } 378 } 379 if (adapter.getSystemConnectionMemo() != null) { 380 stdrows = stdrows + 2; 381 } 382 if (stdrows == NUMOPTIONS) { 383 incAdvancedOptions = false; 384 } 385 _details.setLayout(gbLayout); 386 i = addStandardDetails(incAdvancedOptions, i); 387 if (showAdvanced.isSelected()) { 388 if (isHostNameAdvanced()) { 389 cR.gridy = i; 390 cL.gridy = i; 391 gbLayout.setConstraints(hostNameFieldLabel, cL); 392 gbLayout.setConstraints(hostNameField, cR); 393 _details.add(hostNameFieldLabel); 394 _details.add(hostNameField); 395 i++; 396 } 397 398 if (isPortAdvanced()) { 399 cR.gridy = i; 400 cL.gridy = i; 401 gbLayout.setConstraints(portFieldLabel, cL); 402 gbLayout.setConstraints(portField, cR); 403 _details.add(portFieldLabel); 404 _details.add(portField); 405 i++; 406 } 407 408 if (showAutoConfig.isSelected()) { 409 cR.gridy = i; 410 cL.gridy = i; 411 gbLayout.setConstraints(adNameFieldLabel, cL); 412 gbLayout.setConstraints(adNameField, cR); 413 _details.add(adNameFieldLabel); 414 _details.add(adNameField); 415 i++; 416 cR.gridy = i; 417 cL.gridy = i; 418 gbLayout.setConstraints(serviceTypeFieldLabel, cL); 419 gbLayout.setConstraints(serviceTypeField, cR); 420 _details.add(serviceTypeFieldLabel); 421 _details.add(serviceTypeField); 422 i++; 423 } 424 425 for (Map.Entry<String, Option> entry : options.entrySet()) { 426 if (entry.getValue().isAdvanced()) { 427 cR.gridy = i; 428 cL.gridy = i; 429 gbLayout.setConstraints(entry.getValue().getLabel(), cL); 430 gbLayout.setConstraints(entry.getValue().getComponent(), cR); 431 _details.add(entry.getValue().getLabel()); 432 _details.add(entry.getValue().getComponent()); 433 i++; 434 } 435 } 436 // interval config field 437 cR.gridy = i; 438 cL.gridy = i; 439 gbLayout.setConstraints(outputIntervalLabel, cL); 440 _details.add(outputIntervalLabel); 441 JPanel intervalPanel = new JPanel(); 442 gbLayout.setConstraints(intervalPanel, cR); 443 intervalPanel.add(outputIntervalSpinner); 444 intervalPanel.add(outputIntervalReset); 445 _details.add(intervalPanel); 446 i++; 447 } 448 cL.gridwidth = 2; 449 for (JComponent item : additionalItems) { 450 cL.gridy = i; 451 gbLayout.setConstraints(item, cL); 452 _details.add(item); 453 i++; 454 } 455 cL.gridwidth = 1; 456 if (_details.getParent() != null && _details.getParent() instanceof javax.swing.JViewport) { 457 javax.swing.JViewport vp = (javax.swing.JViewport) _details.getParent(); 458 vp.revalidate(); 459 vp.repaint(); 460 } 461 } 462 463 protected int addStandardDetails(boolean incAdvanced, int i) { 464 465 if (isAutoConfigPossible()) { 466 cR.gridy = i; 467 cL.gridy = i; 468 gbLayout.setConstraints(showAutoConfig, cR); 469 _details.add(showAutoConfig); 470 _details.add(showAutoConfig); 471 i++; 472 } 473 474 if (!isHostNameAdvanced()) { 475 cR.gridy = i; 476 cL.gridy = i; 477 gbLayout.setConstraints(hostNameFieldLabel, cL); 478 gbLayout.setConstraints(hostNameField, cR); 479 _details.add(hostNameFieldLabel); 480 _details.add(hostNameField); 481 i++; 482 } 483 484 if (!isPortAdvanced()) { 485 cR.gridy = i; 486 cL.gridy = i; 487 gbLayout.setConstraints(portFieldLabel, cL); 488 gbLayout.setConstraints(portField, cR); 489 _details.add(portFieldLabel); 490 _details.add(portField); 491 i++; 492 } 493 return addStandardDetails(adapter, incAdvanced, i); 494 } 495 496 public boolean isHostNameAdvanced() { 497 return false; 498 } 499 500 /** 501 * Determine whether to display port in Advanced options. 502 * <p> 503 * Default in Abstract Net Conn Config. Abstract True. 504 * @return true to display port in advanced options. 505 */ 506 public boolean isPortAdvanced() { 507 return true; 508 } 509 510 public boolean isAutoConfigPossible() { 511 return false; 512 } 513 514 public void setAutoNetworkConfig() { 515 if (showAutoConfig.isSelected()) { 516 portField.setEnabled(false); 517 portFieldLabel.setEnabled(false); 518 adapter.setMdnsConfigure(true); 519 } else { 520 portField.setEnabled(true); 521 portFieldLabel.setEnabled(true); 522 adapter.setMdnsConfigure(false); 523 } 524 } 525 526 @Override 527 public String getManufacturer() { 528 return adapter.getManufacturer(); 529 } 530 531 @Override 532 public void setManufacturer(String manufacturer) { 533 setInstance(); 534 adapter.setManufacturer(manufacturer); 535 } 536 537 @Override 538 public boolean getDisabled() { 539 if (adapter == null) { 540 return true; 541 } 542 return adapter.getDisabled(); 543 } 544 545 @Override 546 public void setDisabled(boolean disabled) { 547 if (adapter != null) { 548 adapter.setDisabled(disabled); 549 } 550 } 551 552 @Override 553 public String getConnectionName() { 554 if (adapter.getSystemConnectionMemo() != null) { 555 return adapter.getSystemConnectionMemo().getUserName(); 556 } else { 557 return name(); 558 } 559 } 560 561 @Override 562 public void dispose() { 563 super.dispose(); 564 if (adapter != null) { 565 adapter.dispose(); 566 adapter = null; 567 } 568 } 569 570 private final static Logger log = LoggerFactory.getLogger(AbstractNetworkConnectionConfig.class); 571 572}