001package jmri.jmrix; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004 005import java.awt.*; 006import java.awt.event.FocusEvent; 007import java.awt.event.FocusListener; 008import java.util.ArrayList; 009import java.util.Map; 010import java.util.TreeMap; 011import javax.annotation.Nonnull; 012import javax.swing.*; 013 014import jmri.InstanceManager; 015import jmri.jmrix.configurexml.AbstractConnectionConfigXml; 016import jmri.util.swing.JmriJOptionPane; 017import jmri.util.swing.ValidatedTextField; 018 019/** 020 * Abstract base class for common implementation of the ConnectionConfig. 021 * 022 * @author Bob Jacobsen Copyright (C) 2001, 2003 023 */ 024abstract public class AbstractConnectionConfig implements ConnectionConfig { 025 026 /** 027 * Ctor for a functional object with no preexisting adapter. Expect that the 028 * subclass setInstance() will fill the adapter member. 029 * {@link AbstractConnectionConfigXml}loadCommon 030 */ 031 public AbstractConnectionConfig() { 032 try { 033 systemPrefixField = new ValidatedTextField(4, 034 true, 035 "[A-Za-z]\\d*", 036 Bundle.getMessage("TipPrefixFormat")); 037 // see the "Prefix Needs Migration" dialog in jmri.jmrix.configurexml.AbstractConnectionConfigXml#loadCommon 038 } catch (java.util.regex.PatternSyntaxException e) { 039 log.error("Prefix unexpected parse exception during setup", e); 040 } 041 } 042 043 /** 044 * Complete connection adapter initialization, adding desired options to the 045 * Connection Configuration pane. Required action: set init to true. 046 * Optional actions: 047 * <ul> 048 * <li>fill in connectionNameField</li> 049 * <li>add ActionListeners to config fields eg. systemPrefixField to update adapter after change by the user</li> 050 * </ul> 051 */ 052 abstract protected void checkInitDone(); 053 054 abstract public void updateAdapter(); 055 056 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "Field used by implementing classes") 057 protected int NUMOPTIONS = 2; 058 059 // Load localized field names 060 protected JCheckBox showAdvanced = new JCheckBox(Bundle.getMessage("AdditionalConnectionSettings")); 061 protected JLabel systemPrefixLabel = new JLabel(Bundle.getMessage("ConnectionPrefix")); 062 protected JLabel connectionNameLabel = new JLabel(Bundle.getMessage("ConnectionName")); 063 protected ValidatedTextField systemPrefixField; 064 protected JTextField connectionNameField = new JTextField(15); 065 066 protected JPanel _details = null; 067 068 protected final Map<String, Option> options = new TreeMap<>(); 069 070 /** 071 * Determine if configuration needs to be written to disk. 072 * <p> 073 * This default implementation always returns true to maintain the existing 074 * behavior. 075 * 076 * @return true if configuration need to be saved, false otherwise 077 */ 078 @Override 079 public boolean isDirty() { 080 return (this.getAdapter() == null || this.getAdapter().isDirty()); 081 } 082 083 /** 084 * Determine if application needs to be restarted for configuration changes 085 * to be applied. 086 * <p> 087 * The default implementation always returns true to maintain the existing 088 * behavior. 089 * 090 * @return true if application needs to restart, false otherwise 091 */ 092 @Override 093 public boolean isRestartRequired() { 094 return (this.getAdapter() == null || this.getAdapter().isRestartRequired()); 095 } 096 097 protected static class Option { 098 099 String optionDisplayName; 100 JComponent optionSelection; 101 Boolean advanced; 102 JLabel label = null; 103 104 public Option(String name, JComponent optionSelection, Boolean advanced) { 105 this.optionDisplayName = name; 106 this.optionSelection = optionSelection; 107 this.advanced = advanced; 108 } 109 110 protected String getDisplayName() { 111 return optionDisplayName; 112 } 113 114 public JLabel getLabel() { 115 if (label == null) { 116 label = new JLabel(getDisplayName(), JLabel.LEFT); 117 } 118 return label; 119 } 120 121 public JComponent getComponent() { 122 return optionSelection; 123 } 124 125 protected Boolean isAdvanced() { 126 return advanced; 127 } 128 129 protected void setAdvanced(Boolean boo) { 130 advanced = boo; 131 } 132 133 @SuppressWarnings("unchecked") 134 public String getItem() { 135 if (optionSelection instanceof JComboBox) { 136 return (String) ((JComboBox<String>) optionSelection).getSelectedItem(); 137 } else if (optionSelection instanceof JTextField) { 138 return ((JTextField) optionSelection).getText(); 139 } 140 return null; 141 } 142 } 143 144 /** 145 * Load the adapter with an appropriate object 146 * <i>unless</i> it's already been set. 147 */ 148 abstract protected void setInstance(); 149 150 @Override 151 abstract public String getInfo(); 152 153 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "Field used by implementing classes") 154 protected ArrayList<JComponent> additionalItems = new ArrayList<>(0); 155 156 /** 157 * Load the Swing widgets needed to configure this connection into a 158 * specified JPanel. Used during the configuration process to fill out the 159 * preferences window with content specific to this Connection type. The 160 * JPanel contents need to handle their own gets/sets to the underlying 161 * Connection content. 162 * 163 * @param details the specific Swing object to be configured and filled 164 */ 165 @Override 166 abstract public void loadDetails(final JPanel details); 167 168 protected GridBagLayout gbLayout = new GridBagLayout(); 169 protected GridBagConstraints cL = new GridBagConstraints(); 170 protected GridBagConstraints cR = new GridBagConstraints(); 171 172 abstract protected void showAdvancedItems(); 173 174 protected int addStandardDetails(PortAdapter adapter, boolean incAdvanced, int i) { 175 for (Map.Entry<String, Option> entry : options.entrySet()) { 176 if (!entry.getValue().isAdvanced()) { 177 cR.gridy = i; 178 cL.gridy = i; 179 gbLayout.setConstraints(entry.getValue().getLabel(), cL); 180 gbLayout.setConstraints(entry.getValue().getComponent(), cR); 181 _details.add(entry.getValue().getLabel()); 182 _details.add(entry.getValue().getComponent()); 183 i++; 184 } 185 } 186 187 if (adapter.getSystemConnectionMemo() != null) { 188 cR.gridy = i; 189 cL.gridy = i; 190 gbLayout.setConstraints(systemPrefixLabel, cL); 191 gbLayout.setConstraints(systemPrefixField, cR); 192 systemPrefixLabel.setLabelFor(systemPrefixField); 193 _details.add(systemPrefixLabel); 194 _details.add(systemPrefixField); 195 systemPrefixField.setToolTipText(Bundle.getMessage("TipPrefixFormat")); 196 i++; 197 cR.gridy = i; 198 cL.gridy = i; 199 gbLayout.setConstraints(connectionNameLabel, cL); 200 gbLayout.setConstraints(connectionNameField, cR); 201 connectionNameLabel.setLabelFor(connectionNameField); 202 _details.add(connectionNameLabel); 203 _details.add(connectionNameField); 204 i++; 205 } 206 if (incAdvanced) { 207 cL.gridwidth = 2; 208 cL.gridy = i; 209 cR.gridy = i; 210 gbLayout.setConstraints(showAdvanced, cL); 211 _details.add(showAdvanced); 212 cL.gridwidth = 1; 213 i++; 214 } 215 return i; 216 } 217 218 @Override 219 abstract public String getManufacturer(); 220 221 @Override 222 abstract public void setManufacturer(String manufacturer); 223 224 @Override 225 abstract public String getConnectionName(); 226 227 @Override 228 abstract public boolean getDisabled(); 229 230 @Override 231 abstract public void setDisabled(boolean disable); 232 233 /** 234 * {@inheritDoc} 235 */ 236 @Override 237 public void register() { 238 this.setInstance(); 239 InstanceManager.getDefault(jmri.ConfigureManager.class).registerPref(this); 240 ConnectionConfigManager ccm = InstanceManager.getNullableDefault(ConnectionConfigManager.class); 241 if (ccm != null) { 242 ccm.add(this); 243 } 244 } 245 246 @Override 247 public void dispose() { 248 ConnectionConfigManager ccm = InstanceManager.getNullableDefault(ConnectionConfigManager.class); 249 if (ccm != null) { 250 ccm.remove(this); 251 } 252 } 253 254 protected void addNameEntryCheckers(@Nonnull PortAdapter adapter) { 255 if (adapter.getSystemConnectionMemo() != null) { 256 systemPrefixField.addActionListener(e -> checkPrefixEntry(adapter)); 257 systemPrefixField.addFocusListener(new FocusListener() { 258 @Override 259 public void focusLost(FocusEvent e) { 260 checkPrefixEntry(adapter); 261 } 262 263 @Override 264 public void focusGained(FocusEvent e) { 265 } 266 }); 267 connectionNameField.addActionListener(e -> checkNameEntry(adapter)); 268 connectionNameField.addFocusListener(new FocusListener() { 269 @Override 270 public void focusLost(FocusEvent e) { 271 checkNameEntry(adapter); 272 } 273 274 @Override 275 public void focusGained(FocusEvent e) { 276 } 277 }); 278 } 279 } 280 281 private void checkPrefixEntry(@Nonnull PortAdapter adapter) { 282 if (!systemPrefixField.isValid()) { // invalid prefix format entry, actually can't lose focus until valid 283 systemPrefixField.setText(adapter.getSystemConnectionMemo().getSystemPrefix()); 284 } 285 if (!adapter.getSystemConnectionMemo().setSystemPrefix(systemPrefixField.getText())) { // in use 286 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("ConnectionPrefixDialog", systemPrefixField.getText())); 287 systemPrefixField.setText(adapter.getSystemConnectionMemo().getSystemPrefix()); 288 } 289 } 290 291 private void checkNameEntry(@Nonnull PortAdapter adapter) { 292 if (!adapter.getSystemConnectionMemo().setUserName(connectionNameField.getText())) { 293 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("ConnectionNameDialog", connectionNameField.getText())); 294 connectionNameField.setText(adapter.getSystemConnectionMemo().getUserName()); 295 } 296 } 297 298 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractConnectionConfig.class); 299 300}