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