[2801] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
| 2 | package org.openstreetmap.josm.gui.preferences.server;
|
---|
| 3 |
|
---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 5 |
|
---|
| 6 | import java.awt.Font;
|
---|
| 7 | import java.awt.GridBagConstraints;
|
---|
| 8 | import java.awt.GridBagLayout;
|
---|
| 9 | import java.awt.Insets;
|
---|
| 10 | import java.awt.event.ActionEvent;
|
---|
| 11 | import java.awt.event.ActionListener;
|
---|
| 12 | import java.awt.event.FocusAdapter;
|
---|
| 13 | import java.awt.event.FocusEvent;
|
---|
| 14 | import java.awt.event.ItemEvent;
|
---|
| 15 | import java.awt.event.ItemListener;
|
---|
| 16 | import java.net.MalformedURLException;
|
---|
| 17 | import java.net.URL;
|
---|
| 18 |
|
---|
| 19 | import javax.swing.AbstractAction;
|
---|
| 20 | import javax.swing.JCheckBox;
|
---|
[6653] | 21 | import javax.swing.JComponent;
|
---|
[2801] | 22 | import javax.swing.JLabel;
|
---|
| 23 | import javax.swing.JPanel;
|
---|
| 24 | import javax.swing.SwingUtilities;
|
---|
| 25 | import javax.swing.event.DocumentEvent;
|
---|
| 26 | import javax.swing.event.DocumentListener;
|
---|
| 27 | import javax.swing.text.JTextComponent;
|
---|
| 28 |
|
---|
| 29 | import org.openstreetmap.josm.Main;
|
---|
| 30 | import org.openstreetmap.josm.gui.SideButton;
|
---|
| 31 | import org.openstreetmap.josm.gui.help.HelpUtil;
|
---|
| 32 | import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
|
---|
[6529] | 33 | import org.openstreetmap.josm.gui.widgets.JosmTextField;
|
---|
[2801] | 34 | import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
|
---|
[3934] | 35 | import org.openstreetmap.josm.io.OsmApi;
|
---|
[2801] | 36 | import org.openstreetmap.josm.tools.ImageProvider;
|
---|
[6602] | 37 | import org.openstreetmap.josm.tools.Utils;
|
---|
[2801] | 38 |
|
---|
[6529] | 39 | /**
|
---|
| 40 | * Component allowing input os OSM API URL.
|
---|
| 41 | */
|
---|
[2801] | 42 | public class OsmApiUrlInputPanel extends JPanel {
|
---|
[6814] | 43 |
|
---|
[6529] | 44 | /**
|
---|
| 45 | * OSM API URL property key.
|
---|
| 46 | */
|
---|
[6883] | 47 | public static final String API_URL_PROP = OsmApiUrlInputPanel.class.getName() + ".apiUrl";
|
---|
[2801] | 48 |
|
---|
| 49 | private JLabel lblValid;
|
---|
| 50 | private JLabel lblApiUrl;
|
---|
[5886] | 51 | private JosmTextField tfOsmServerUrl;
|
---|
[8308] | 52 | private transient ApiUrlValidator valOsmServerUrl;
|
---|
[2801] | 53 | private SideButton btnTest;
|
---|
| 54 | /** indicates whether to use the default OSM URL or not */
|
---|
| 55 | private JCheckBox cbUseDefaultServerUrl;
|
---|
[6814] | 56 |
|
---|
[8308] | 57 | private transient ApiUrlPropagator propagator;
|
---|
[2801] | 58 |
|
---|
[6653] | 59 | protected JComponent buildDefaultServerUrlPanel() {
|
---|
| 60 | cbUseDefaultServerUrl = new JCheckBox(tr("<html>Use the default OSM server URL (<strong>{0}</strong>)</html>", OsmApi.DEFAULT_API_URL));
|
---|
[2801] | 61 | cbUseDefaultServerUrl.addItemListener(new UseDefaultServerUrlChangeHandler());
|
---|
[6653] | 62 | cbUseDefaultServerUrl.setFont(cbUseDefaultServerUrl.getFont().deriveFont(Font.PLAIN));
|
---|
| 63 | return cbUseDefaultServerUrl;
|
---|
[2801] | 64 | }
|
---|
| 65 |
|
---|
[6890] | 66 | protected final void build() {
|
---|
[2801] | 67 | setLayout(new GridBagLayout());
|
---|
| 68 | GridBagConstraints gc = new GridBagConstraints();
|
---|
| 69 |
|
---|
| 70 | // the checkbox for the default UL
|
---|
| 71 | gc.fill = GridBagConstraints.HORIZONTAL;
|
---|
| 72 | gc.anchor = GridBagConstraints.NORTHWEST;
|
---|
| 73 | gc.weightx = 1.0;
|
---|
[8510] | 74 | gc.insets = new Insets(0, 0, 0, 0);
|
---|
[2801] | 75 | gc.gridwidth = 4;
|
---|
[6602] | 76 | add(buildDefaultServerUrlPanel(), gc);
|
---|
[2801] | 77 |
|
---|
| 78 |
|
---|
| 79 | // the input field for the URL
|
---|
| 80 | gc.gridx = 0;
|
---|
| 81 | gc.gridy = 1;
|
---|
| 82 | gc.gridwidth = 1;
|
---|
| 83 | gc.weightx = 0.0;
|
---|
[8510] | 84 | gc.insets = new Insets(0, 0, 0, 3);
|
---|
[2801] | 85 | add(lblApiUrl = new JLabel(tr("OSM Server URL:")), gc);
|
---|
| 86 |
|
---|
| 87 | gc.gridx = 1;
|
---|
| 88 | gc.weightx = 1.0;
|
---|
[5886] | 89 | add(tfOsmServerUrl = new JosmTextField(), gc);
|
---|
[8426] | 90 | lblApiUrl.setLabelFor(tfOsmServerUrl);
|
---|
[2801] | 91 | SelectAllOnFocusGainedDecorator.decorate(tfOsmServerUrl);
|
---|
| 92 | valOsmServerUrl = new ApiUrlValidator(tfOsmServerUrl);
|
---|
| 93 | valOsmServerUrl.validate();
|
---|
[6602] | 94 | propagator = new ApiUrlPropagator();
|
---|
[2801] | 95 | tfOsmServerUrl.addActionListener(propagator);
|
---|
| 96 | tfOsmServerUrl.addFocusListener(propagator);
|
---|
| 97 |
|
---|
| 98 | gc.gridx = 2;
|
---|
| 99 | gc.weightx = 0.0;
|
---|
| 100 | add(lblValid = new JLabel(), gc);
|
---|
| 101 |
|
---|
| 102 | gc.gridx = 3;
|
---|
| 103 | gc.weightx = 0.0;
|
---|
| 104 | ValidateApiUrlAction actTest = new ValidateApiUrlAction();
|
---|
| 105 | tfOsmServerUrl.getDocument().addDocumentListener(actTest);
|
---|
| 106 | add(btnTest = new SideButton(actTest), gc);
|
---|
| 107 | }
|
---|
| 108 |
|
---|
[6296] | 109 | /**
|
---|
| 110 | * Constructs a new {@code OsmApiUrlInputPanel}.
|
---|
| 111 | */
|
---|
[2801] | 112 | public OsmApiUrlInputPanel() {
|
---|
| 113 | build();
|
---|
| 114 | HelpUtil.setHelpContext(this, HelpUtil.ht("/Preferences/Connection#ApiUrl"));
|
---|
| 115 | }
|
---|
| 116 |
|
---|
| 117 | /**
|
---|
| 118 | * Initializes the configuration panel with values from the preferences
|
---|
| 119 | */
|
---|
| 120 | public void initFromPreferences() {
|
---|
[6582] | 121 | String url = Main.pref.get("osm-server.url", OsmApi.DEFAULT_API_URL);
|
---|
[8373] | 122 | if (OsmApi.DEFAULT_API_URL.equals(url.trim())) {
|
---|
[2801] | 123 | cbUseDefaultServerUrl.setSelected(true);
|
---|
[6602] | 124 | propagator.propagate(OsmApi.DEFAULT_API_URL);
|
---|
[2801] | 125 | } else {
|
---|
| 126 | cbUseDefaultServerUrl.setSelected(false);
|
---|
| 127 | tfOsmServerUrl.setText(url);
|
---|
[6602] | 128 | propagator.propagate(url);
|
---|
[2801] | 129 | }
|
---|
| 130 | }
|
---|
| 131 |
|
---|
| 132 | /**
|
---|
| 133 | * Saves the values to the preferences
|
---|
| 134 | */
|
---|
| 135 | public void saveToPreferences() {
|
---|
[6602] | 136 | String oldUrl = Main.pref.get("osm-server.url", OsmApi.DEFAULT_API_URL);
|
---|
| 137 | String hmiUrl = getStrippedApiUrl();
|
---|
[2801] | 138 | if (cbUseDefaultServerUrl.isSelected()) {
|
---|
| 139 | Main.pref.put("osm-server.url", null);
|
---|
[8373] | 140 | } else if (OsmApi.DEFAULT_API_URL.equals(hmiUrl)) {
|
---|
[2801] | 141 | Main.pref.put("osm-server.url", null);
|
---|
| 142 | } else {
|
---|
[6602] | 143 | Main.pref.put("osm-server.url", hmiUrl);
|
---|
[2801] | 144 | }
|
---|
[6602] | 145 | String newUrl = Main.pref.get("osm-server.url", OsmApi.DEFAULT_API_URL);
|
---|
[2801] | 146 |
|
---|
[3934] | 147 | // When API URL changes, re-initialize API connection so we may adjust
|
---|
| 148 | // server-dependent settings.
|
---|
[6602] | 149 | if (!oldUrl.equals(newUrl)) {
|
---|
[3934] | 150 | try {
|
---|
| 151 | OsmApi.getOsmApi().initialize(null);
|
---|
| 152 | } catch (Exception x) {
|
---|
[6296] | 153 | Main.warn(x);
|
---|
[3934] | 154 | }
|
---|
| 155 | }
|
---|
[2801] | 156 | }
|
---|
[6814] | 157 |
|
---|
[6602] | 158 | /**
|
---|
| 159 | * Returns the entered API URL, stripped of leading and trailing white characters.
|
---|
[6814] | 160 | * @return the entered API URL, stripped of leading and trailing white characters.
|
---|
[6602] | 161 | * May be an empty string if nothing has been entered. In this case, it means the user wants to use {@link OsmApi#DEFAULT_API_URL}.
|
---|
| 162 | * @see Utils#strip(String)
|
---|
| 163 | * @since 6602
|
---|
| 164 | */
|
---|
| 165 | public final String getStrippedApiUrl() {
|
---|
| 166 | return Utils.strip(tfOsmServerUrl.getText());
|
---|
| 167 | }
|
---|
[2801] | 168 |
|
---|
| 169 | class ValidateApiUrlAction extends AbstractAction implements DocumentListener {
|
---|
[8840] | 170 | private String lastTestedUrl;
|
---|
[2801] | 171 |
|
---|
[8836] | 172 | ValidateApiUrlAction() {
|
---|
[2801] | 173 | putValue(NAME, tr("Validate"));
|
---|
| 174 | putValue(SHORT_DESCRIPTION, tr("Test the API URL"));
|
---|
| 175 | updateEnabledState();
|
---|
| 176 | }
|
---|
| 177 |
|
---|
[6084] | 178 | @Override
|
---|
[2801] | 179 | public void actionPerformed(ActionEvent arg0) {
|
---|
[6602] | 180 | final String url = getStrippedApiUrl();
|
---|
[2801] | 181 | final ApiUrlTestTask task = new ApiUrlTestTask(OsmApiUrlInputPanel.this, url);
|
---|
| 182 | Main.worker.submit(task);
|
---|
| 183 | Runnable r = new Runnable() {
|
---|
[6084] | 184 | @Override
|
---|
[2801] | 185 | public void run() {
|
---|
| 186 | if (task.isCanceled())
|
---|
| 187 | return;
|
---|
| 188 | Runnable r = new Runnable() {
|
---|
[6084] | 189 | @Override
|
---|
[2801] | 190 | public void run() {
|
---|
| 191 | if (task.isSuccess()) {
|
---|
[6814] | 192 | lblValid.setIcon(ImageProvider.get("dialogs", "valid"));
|
---|
[2801] | 193 | lblValid.setToolTipText(tr("The API URL is valid."));
|
---|
| 194 | lastTestedUrl = url;
|
---|
| 195 | updateEnabledState();
|
---|
| 196 | } else {
|
---|
| 197 | lblValid.setIcon(ImageProvider.get("warning-small"));
|
---|
| 198 | lblValid.setToolTipText(tr("Validation failed. The API URL seems to be invalid."));
|
---|
| 199 | }
|
---|
| 200 | }
|
---|
| 201 | };
|
---|
| 202 | SwingUtilities.invokeLater(r);
|
---|
| 203 | }
|
---|
| 204 | };
|
---|
| 205 | Main.worker.submit(r);
|
---|
| 206 | }
|
---|
| 207 |
|
---|
[6890] | 208 | protected final void updateEnabledState() {
|
---|
[6602] | 209 | String url = getStrippedApiUrl();
|
---|
| 210 | boolean enabled = !url.isEmpty() && !url.equals(lastTestedUrl);
|
---|
[2801] | 211 | if (enabled) {
|
---|
| 212 | lblValid.setIcon(null);
|
---|
| 213 | }
|
---|
| 214 | setEnabled(enabled);
|
---|
| 215 | }
|
---|
| 216 |
|
---|
[6084] | 217 | @Override
|
---|
[2801] | 218 | public void changedUpdate(DocumentEvent arg0) {
|
---|
| 219 | updateEnabledState();
|
---|
| 220 | }
|
---|
| 221 |
|
---|
[6084] | 222 | @Override
|
---|
[2801] | 223 | public void insertUpdate(DocumentEvent arg0) {
|
---|
| 224 | updateEnabledState();
|
---|
| 225 | }
|
---|
| 226 |
|
---|
[6084] | 227 | @Override
|
---|
[2801] | 228 | public void removeUpdate(DocumentEvent arg0) {
|
---|
| 229 | updateEnabledState();
|
---|
| 230 | }
|
---|
| 231 | }
|
---|
| 232 |
|
---|
[6529] | 233 | /**
|
---|
| 234 | * Enables or disables the API URL input.
|
---|
| 235 | * @param enabled {@code true} to enable input, {@code false} otherwise
|
---|
| 236 | */
|
---|
[2801] | 237 | public void setApiUrlInputEnabled(boolean enabled) {
|
---|
| 238 | lblApiUrl.setEnabled(enabled);
|
---|
| 239 | tfOsmServerUrl.setEnabled(enabled);
|
---|
| 240 | lblValid.setEnabled(enabled);
|
---|
| 241 | btnTest.setEnabled(enabled);
|
---|
| 242 | }
|
---|
| 243 |
|
---|
[6883] | 244 | private static class ApiUrlValidator extends AbstractTextComponentValidator {
|
---|
[8836] | 245 | ApiUrlValidator(JTextComponent tc) {
|
---|
[2801] | 246 | super(tc);
|
---|
| 247 | }
|
---|
| 248 |
|
---|
| 249 | @Override
|
---|
| 250 | public boolean isValid() {
|
---|
[6087] | 251 | if (getComponent().getText().trim().isEmpty())
|
---|
[2801] | 252 | return false;
|
---|
| 253 |
|
---|
| 254 | try {
|
---|
| 255 | new URL(getComponent().getText().trim());
|
---|
| 256 | return true;
|
---|
[8510] | 257 | } catch (MalformedURLException e) {
|
---|
[2801] | 258 | return false;
|
---|
| 259 | }
|
---|
| 260 | }
|
---|
| 261 |
|
---|
| 262 | @Override
|
---|
| 263 | public void validate() {
|
---|
[6087] | 264 | if (getComponent().getText().trim().isEmpty()) {
|
---|
[2801] | 265 | feedbackInvalid(tr("OSM API URL must not be empty. Please enter the OSM API URL."));
|
---|
| 266 | return;
|
---|
| 267 | }
|
---|
| 268 | if (!isValid()) {
|
---|
[2849] | 269 | feedbackInvalid(tr("The current value is not a valid URL"));
|
---|
[2801] | 270 | } else {
|
---|
| 271 | feedbackValid(tr("Please enter the OSM API URL."));
|
---|
| 272 | }
|
---|
| 273 | }
|
---|
| 274 | }
|
---|
| 275 |
|
---|
| 276 | /**
|
---|
| 277 | * Handles changes in the default URL
|
---|
| 278 | */
|
---|
| 279 | class UseDefaultServerUrlChangeHandler implements ItemListener {
|
---|
[6084] | 280 | @Override
|
---|
[2801] | 281 | public void itemStateChanged(ItemEvent e) {
|
---|
| 282 | switch(e.getStateChange()) {
|
---|
| 283 | case ItemEvent.SELECTED:
|
---|
| 284 | setApiUrlInputEnabled(false);
|
---|
[6602] | 285 | propagator.propagate(OsmApi.DEFAULT_API_URL);
|
---|
[2801] | 286 | break;
|
---|
| 287 | case ItemEvent.DESELECTED:
|
---|
| 288 | setApiUrlInputEnabled(true);
|
---|
| 289 | valOsmServerUrl.validate();
|
---|
| 290 | tfOsmServerUrl.requestFocusInWindow();
|
---|
[6602] | 291 | propagator.propagate();
|
---|
[2801] | 292 | break;
|
---|
| 293 | }
|
---|
| 294 | }
|
---|
| 295 | }
|
---|
| 296 |
|
---|
| 297 | class ApiUrlPropagator extends FocusAdapter implements ActionListener {
|
---|
| 298 | public void propagate() {
|
---|
[6602] | 299 | propagate(getStrippedApiUrl());
|
---|
[2801] | 300 | }
|
---|
| 301 |
|
---|
[6602] | 302 | public void propagate(String url) {
|
---|
| 303 | firePropertyChange(API_URL_PROP, null, url);
|
---|
| 304 | }
|
---|
| 305 |
|
---|
[6084] | 306 | @Override
|
---|
[2801] | 307 | public void actionPerformed(ActionEvent e) {
|
---|
| 308 | propagate();
|
---|
| 309 | }
|
---|
| 310 |
|
---|
| 311 | @Override
|
---|
| 312 | public void focusLost(FocusEvent arg0) {
|
---|
| 313 | propagate();
|
---|
| 314 | }
|
---|
| 315 | }
|
---|
| 316 | }
|
---|