001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.gui.preferences.server; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.awt.Font; 007 import java.awt.GridBagConstraints; 008 import java.awt.GridBagLayout; 009 import java.awt.Insets; 010 import java.awt.event.ActionEvent; 011 import java.awt.event.ActionListener; 012 import java.awt.event.FocusAdapter; 013 import java.awt.event.FocusEvent; 014 import java.awt.event.ItemEvent; 015 import java.awt.event.ItemListener; 016 import java.net.MalformedURLException; 017 import java.net.URL; 018 019 import javax.swing.AbstractAction; 020 import javax.swing.JCheckBox; 021 import javax.swing.JLabel; 022 import javax.swing.JPanel; 023 import javax.swing.JTextField; 024 import javax.swing.SwingUtilities; 025 import javax.swing.event.DocumentEvent; 026 import javax.swing.event.DocumentListener; 027 import javax.swing.text.JTextComponent; 028 029 import org.openstreetmap.josm.Main; 030 import org.openstreetmap.josm.gui.SideButton; 031 import org.openstreetmap.josm.gui.help.HelpUtil; 032 import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator; 033 import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator; 034 import org.openstreetmap.josm.io.OsmApi; 035 import org.openstreetmap.josm.tools.ImageProvider; 036 037 public class OsmApiUrlInputPanel extends JPanel { 038 static public final String API_URL_PROP = OsmApiUrlInputPanel.class.getName() + ".apiUrl"; 039 040 private JLabel lblValid; 041 private JLabel lblApiUrl; 042 private JTextField tfOsmServerUrl; 043 private ApiUrlValidator valOsmServerUrl; 044 private SideButton btnTest; 045 /** indicates whether to use the default OSM URL or not */ 046 private JCheckBox cbUseDefaultServerUrl; 047 048 protected JPanel buildDefultServerUrlPanel() { 049 JPanel pnl = new JPanel(new GridBagLayout()); 050 GridBagConstraints gc = new GridBagConstraints(); 051 052 gc.fill = GridBagConstraints.HORIZONTAL; 053 gc.anchor = GridBagConstraints.NORTHWEST; 054 gc.weightx = 0.0; 055 gc.insets = new Insets(0,0,0,3); 056 gc.gridwidth = 1; 057 pnl.add(cbUseDefaultServerUrl = new JCheckBox(), gc); 058 cbUseDefaultServerUrl.addItemListener(new UseDefaultServerUrlChangeHandler()); 059 060 gc.gridx = 1; 061 gc.weightx = 1.0; 062 JLabel lbl = new JLabel(tr("<html>Use the default OSM server URL (<strong>{0}</strong>)</html>", OsmApi.DEFAULT_API_URL)); 063 lbl.setFont(lbl.getFont().deriveFont(Font.PLAIN)); 064 pnl.add(lbl, gc); 065 066 return pnl; 067 } 068 069 protected void build() { 070 setLayout(new GridBagLayout()); 071 GridBagConstraints gc = new GridBagConstraints(); 072 073 // the checkbox for the default UL 074 gc.fill = GridBagConstraints.HORIZONTAL; 075 gc.anchor = GridBagConstraints.NORTHWEST; 076 gc.weightx = 1.0; 077 gc.insets = new Insets(0,0,0,0); 078 gc.gridwidth = 4; 079 add(buildDefultServerUrlPanel(), gc); 080 081 082 // the input field for the URL 083 gc.gridx = 0; 084 gc.gridy = 1; 085 gc.gridwidth = 1; 086 gc.weightx = 0.0; 087 gc.insets = new Insets(0,0,0,3); 088 add(lblApiUrl = new JLabel(tr("OSM Server URL:")), gc); 089 090 gc.gridx = 1; 091 gc.weightx = 1.0; 092 add(tfOsmServerUrl = new JTextField(), gc); 093 SelectAllOnFocusGainedDecorator.decorate(tfOsmServerUrl); 094 valOsmServerUrl = new ApiUrlValidator(tfOsmServerUrl); 095 valOsmServerUrl.validate(); 096 ApiUrlPropagator propagator = new ApiUrlPropagator(); 097 tfOsmServerUrl.addActionListener(propagator); 098 tfOsmServerUrl.addFocusListener(propagator); 099 100 gc.gridx = 2; 101 gc.weightx = 0.0; 102 add(lblValid = new JLabel(), gc); 103 104 gc.gridx = 3; 105 gc.weightx = 0.0; 106 ValidateApiUrlAction actTest = new ValidateApiUrlAction(); 107 tfOsmServerUrl.getDocument().addDocumentListener(actTest); 108 add(btnTest = new SideButton(actTest), gc); 109 } 110 111 public OsmApiUrlInputPanel() { 112 build(); 113 HelpUtil.setHelpContext(this, HelpUtil.ht("/Preferences/Connection#ApiUrl")); 114 } 115 116 /** 117 * Initializes the configuration panel with values from the preferences 118 */ 119 public void initFromPreferences() { 120 String url = Main.pref.get("osm-server.url", null); 121 if (url == null) { 122 cbUseDefaultServerUrl.setSelected(true); 123 firePropertyChange(API_URL_PROP, null, OsmApi.DEFAULT_API_URL); 124 } else if (url.trim().equals(OsmApi.DEFAULT_API_URL)) { 125 cbUseDefaultServerUrl.setSelected(true); 126 firePropertyChange(API_URL_PROP, null, OsmApi.DEFAULT_API_URL); 127 } else { 128 cbUseDefaultServerUrl.setSelected(false); 129 tfOsmServerUrl.setText(url); 130 firePropertyChange(API_URL_PROP, null, url); 131 } 132 } 133 134 /** 135 * Saves the values to the preferences 136 */ 137 public void saveToPreferences() { 138 String old_url = Main.pref.get("osm-server.url", null); 139 if (cbUseDefaultServerUrl.isSelected()) { 140 Main.pref.put("osm-server.url", null); 141 } else if (tfOsmServerUrl.getText().trim().equals(OsmApi.DEFAULT_API_URL)) { 142 Main.pref.put("osm-server.url", null); 143 } else { 144 Main.pref.put("osm-server.url", tfOsmServerUrl.getText().trim()); 145 } 146 String new_url = Main.pref.get("osm-server.url", null); 147 148 // When API URL changes, re-initialize API connection so we may adjust 149 // server-dependent settings. 150 if ((old_url == null && new_url != null) || (old_url != null && !old_url.equals(new_url))) { 151 try { 152 OsmApi.getOsmApi().initialize(null); 153 } catch (Exception x) { 154 // ignore; 155 } 156 } 157 } 158 159 class ValidateApiUrlAction extends AbstractAction implements DocumentListener { 160 private String lastTestedUrl = null; 161 162 public ValidateApiUrlAction() { 163 putValue(NAME, tr("Validate")); 164 putValue(SHORT_DESCRIPTION, tr("Test the API URL")); 165 updateEnabledState(); 166 } 167 168 public void actionPerformed(ActionEvent arg0) { 169 final String url = tfOsmServerUrl.getText().trim(); 170 final ApiUrlTestTask task = new ApiUrlTestTask(OsmApiUrlInputPanel.this, url); 171 Main.worker.submit(task); 172 Runnable r = new Runnable() { 173 public void run() { 174 if (task.isCanceled()) 175 return; 176 Runnable r = new Runnable() { 177 public void run() { 178 if (task.isSuccess()) { 179 lblValid.setIcon(ImageProvider.get("dialogs/changeset", "valid")); 180 lblValid.setToolTipText(tr("The API URL is valid.")); 181 lastTestedUrl = url; 182 updateEnabledState(); 183 } else { 184 lblValid.setIcon(ImageProvider.get("warning-small")); 185 lblValid.setToolTipText(tr("Validation failed. The API URL seems to be invalid.")); 186 } 187 } 188 }; 189 SwingUtilities.invokeLater(r); 190 } 191 }; 192 Main.worker.submit(r); 193 } 194 195 protected void updateEnabledState() { 196 boolean enabled = 197 !tfOsmServerUrl.getText().trim().equals("") 198 && !tfOsmServerUrl.getText().trim().equals(lastTestedUrl); 199 if (enabled) { 200 lblValid.setIcon(null); 201 } 202 setEnabled(enabled); 203 } 204 205 public void changedUpdate(DocumentEvent arg0) { 206 updateEnabledState(); 207 } 208 209 public void insertUpdate(DocumentEvent arg0) { 210 updateEnabledState(); 211 } 212 213 public void removeUpdate(DocumentEvent arg0) { 214 updateEnabledState(); 215 } 216 } 217 218 public void setApiUrlInputEnabled(boolean enabled) { 219 lblApiUrl.setEnabled(enabled); 220 tfOsmServerUrl.setEnabled(enabled); 221 lblValid.setEnabled(enabled); 222 btnTest.setEnabled(enabled); 223 } 224 225 static private class ApiUrlValidator extends AbstractTextComponentValidator { 226 public ApiUrlValidator(JTextComponent tc) throws IllegalArgumentException { 227 super(tc); 228 } 229 230 @Override 231 public boolean isValid() { 232 if (getComponent().getText().trim().equals("")) 233 return false; 234 235 try { 236 new URL(getComponent().getText().trim()); 237 return true; 238 } catch(MalformedURLException e) { 239 return false; 240 } 241 } 242 243 @Override 244 public void validate() { 245 if (getComponent().getText().trim().equals("")) { 246 feedbackInvalid(tr("OSM API URL must not be empty. Please enter the OSM API URL.")); 247 return; 248 } 249 if (!isValid()) { 250 feedbackInvalid(tr("The current value is not a valid URL")); 251 } else { 252 feedbackValid(tr("Please enter the OSM API URL.")); 253 } 254 } 255 } 256 257 /** 258 * Handles changes in the default URL 259 */ 260 class UseDefaultServerUrlChangeHandler implements ItemListener { 261 public void itemStateChanged(ItemEvent e) { 262 switch(e.getStateChange()) { 263 case ItemEvent.SELECTED: 264 setApiUrlInputEnabled(false); 265 firePropertyChange(API_URL_PROP, null, OsmApi.DEFAULT_API_URL); 266 break; 267 case ItemEvent.DESELECTED: 268 setApiUrlInputEnabled(true); 269 valOsmServerUrl.validate(); 270 tfOsmServerUrl.requestFocusInWindow(); 271 firePropertyChange(API_URL_PROP, null, tfOsmServerUrl.getText()); 272 break; 273 } 274 } 275 } 276 277 class ApiUrlPropagator extends FocusAdapter implements ActionListener { 278 public void propagate() { 279 firePropertyChange(API_URL_PROP, null, tfOsmServerUrl.getText()); 280 } 281 282 public void actionPerformed(ActionEvent e) { 283 propagate(); 284 } 285 286 @Override 287 public void focusLost(FocusEvent arg0) { 288 propagate(); 289 } 290 } 291 }