// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.gui.dialogs.changeset.query;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.HyperlinkEvent;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.gui.widgets.HtmlPanel;
import org.openstreetmap.josm.gui.widgets.JosmTextField;
import org.openstreetmap.josm.io.ChangesetQuery;
import org.openstreetmap.josm.io.ChangesetQuery.ChangesetQueryUrlException;
import org.openstreetmap.josm.io.OsmApi;
import org.openstreetmap.josm.tools.ImageProvider;
/**
* This panel allows to build a changeset query from an URL.
* @since 2689
*/
public class UrlBasedQueryPanel extends JPanel {
private final JosmTextField tfUrl = new JosmTextField();
private final JLabel lblValid = new JLabel();
/**
* Constructs a new {@code UrlBasedQueryPanel}.
*/
public UrlBasedQueryPanel() {
build();
}
protected JPanel buildURLPanel() {
JPanel pnl = new JPanel(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
gc.weightx = 0.0;
gc.fill = GridBagConstraints.HORIZONTAL;
gc.insets = new Insets(0, 0, 0, 5);
pnl.add(new JLabel(tr("URL: ")), gc);
gc.gridx = 1;
gc.weightx = 1.0;
gc.fill = GridBagConstraints.HORIZONTAL;
pnl.add(tfUrl, gc);
tfUrl.getDocument().addDocumentListener(new ChangetQueryUrlValidator());
tfUrl.addFocusListener(
new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
tfUrl.selectAll();
}
}
);
gc.gridx = 2;
gc.weightx = 0.0;
gc.fill = GridBagConstraints.HORIZONTAL;
pnl.add(lblValid, gc);
lblValid.setPreferredSize(new Dimension(20, 20));
return pnl;
}
protected JPanel buildHelpPanel() {
String apiUrl = OsmApi.getOsmApi().getBaseUrl();
HtmlPanel pnl = new HtmlPanel();
pnl.setText(
"<html><body>"
+ tr("Please enter or paste an URL to retrieve changesets from the OSM API.")
+ "<p><strong>" + tr("Examples") + "</strong></p>"
+ "<ul>"
+ "<li><a href=\""+Main.getOSMWebsite()+"/history?open=true\">"+Main.getOSMWebsite()+"/history?open=true</a></li>"
+ "<li><a href=\""+apiUrl+"/changesets?open=true\">"+apiUrl+"/changesets?open=true</a></li>"
+ "</ul>"
+ tr("Note that changeset queries are currently always submitted to ''{0}'', regardless of the "
+ "host, port and path of the URL entered below.", apiUrl)
+ "</body></html>"
);
pnl.getEditorPane().addHyperlinkListener(e -> {
if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
tfUrl.setText(e.getDescription());
tfUrl.requestFocusInWindow();
}
});
return pnl;
}
protected final void build() {
setLayout(new GridBagLayout());
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
GridBagConstraints gc = new GridBagConstraints();
gc.weightx = 1.0;
gc.fill = GridBagConstraints.HORIZONTAL;
gc.insets = new Insets(0, 0, 10, 0);
add(buildHelpPanel(), gc);
gc.gridy = 1;
gc.weightx = 1.0;
gc.fill = GridBagConstraints.HORIZONTAL;
add(buildURLPanel(), gc);
gc.gridy = 2;
gc.weightx = 1.0;
gc.weighty = 1.0;
gc.fill = GridBagConstraints.BOTH;
add(new JPanel(), gc);
}
protected boolean isValidChangesetQueryUrl(String text) {
return buildChangesetQuery(text) != null;
}
protected ChangesetQuery buildChangesetQuery(String text) {
URL url = null;
try {
url = new URL(text);
} catch (MalformedURLException e) {
return null;
}
String path = url.getPath();
if (path == null || !path.endsWith("/changesets"))
return null;
try {
return ChangesetQuery.buildFromUrlQuery(url.getQuery());
} catch (ChangesetQueryUrlException e) {
Main.warn(e);
return null;
}
}
/**
* Replies the {@link ChangesetQuery} specified in this panel. null, if no valid changeset query
* is specified.
*
* @return the changeset query
*/
public ChangesetQuery buildChangesetQuery() {
String value = tfUrl.getText().trim();
return buildChangesetQuery(value);
}
/**
* Initializes HMI for user input.
*/
public void startUserInput() {
tfUrl.requestFocusInWindow();
}
/**
* Validates text entered in the changeset query URL field on the fly
*/
class ChangetQueryUrlValidator implements DocumentListener {
protected String getCurrentFeedback() {
String fb = (String) lblValid.getClientProperty("valid");
return fb == null ? "none" : fb;
}
protected void feedbackValid() {
if ("valid".equals(getCurrentFeedback()))
return;
lblValid.setIcon(ImageProvider.get("dialogs", "valid"));
lblValid.setToolTipText(null);
lblValid.putClientProperty("valid", "valid");
}
protected void feedbackInvalid() {
if ("invalid".equals(getCurrentFeedback()))
return;
lblValid.setIcon(ImageProvider.get("warning-small"));
lblValid.setToolTipText(tr("This changeset query URL is invalid"));
lblValid.putClientProperty("valid", "invalid");
}
protected void feedbackNone() {
lblValid.setIcon(null);
lblValid.putClientProperty("valid", "none");
}
protected void validate() {
String value = tfUrl.getText();
if (value.trim().isEmpty()) {
feedbackNone();
return;
}
value = value.trim();
if (isValidChangesetQueryUrl(value)) {
feedbackValid();
} else {
feedbackInvalid();
}
}
@Override
public void changedUpdate(DocumentEvent e) {
validate();
}
@Override
public void insertUpdate(DocumentEvent e) {
validate();
}
@Override
public void removeUpdate(DocumentEvent e) {
validate();
}
}
}