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 }