001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.changeset.query; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Dimension; 007import java.awt.GridBagConstraints; 008import java.awt.GridBagLayout; 009import java.awt.Insets; 010import java.awt.event.FocusAdapter; 011import java.awt.event.FocusEvent; 012import java.net.MalformedURLException; 013import java.net.URL; 014 015import javax.swing.BorderFactory; 016import javax.swing.JLabel; 017import javax.swing.JPanel; 018import javax.swing.event.DocumentEvent; 019import javax.swing.event.DocumentListener; 020import javax.swing.event.HyperlinkEvent; 021import javax.swing.event.HyperlinkListener; 022 023import org.openstreetmap.josm.Main; 024import org.openstreetmap.josm.gui.widgets.HtmlPanel; 025import org.openstreetmap.josm.io.ChangesetQuery; 026import org.openstreetmap.josm.io.OsmApi; 027import org.openstreetmap.josm.io.ChangesetQuery.ChangesetQueryUrlException; 028import org.openstreetmap.josm.tools.ImageProvider; 029import org.openstreetmap.josm.gui.widgets.JosmTextField; 030 031 032public class UrlBasedQueryPanel extends JPanel { 033 034 private JosmTextField tfUrl; 035 private JLabel lblValid; 036 037 protected JPanel buildURLPanel() { 038 JPanel pnl = new JPanel(new GridBagLayout()); 039 GridBagConstraints gc = new GridBagConstraints(); 040 gc.weightx = 0.0; 041 gc.fill = GridBagConstraints.HORIZONTAL; 042 gc.insets = new Insets(0,0,0,5); 043 pnl.add(new JLabel(tr("URL: ")), gc); 044 045 gc.gridx = 1; 046 gc.weightx = 1.0; 047 gc.fill = GridBagConstraints.HORIZONTAL; 048 pnl.add(tfUrl = new JosmTextField(), gc); 049 tfUrl.getDocument().addDocumentListener(new ChangetQueryUrlValidator()); 050 tfUrl.addFocusListener( 051 new FocusAdapter() { 052 @Override 053 public void focusGained(FocusEvent e) { 054 tfUrl.selectAll(); 055 } 056 } 057 ); 058 059 gc.gridx = 2; 060 gc.weightx = 0.0; 061 gc.fill = GridBagConstraints.HORIZONTAL; 062 pnl.add(lblValid = new JLabel(), gc); 063 lblValid.setPreferredSize(new Dimension(20,20)); 064 return pnl; 065 } 066 067 protected JPanel buildHelpPanel() { 068 HtmlPanel pnl = new HtmlPanel(); 069 pnl.setText( 070 "<html><body>" 071 + tr("Please enter or paste an URL to retrieve changesets from the OSM API.") 072 + "<p><strong>" + tr("Examples") + "</strong></p>" 073 + "<ul>" 074 + "<li><a href=\""+Main.OSM_WEBSITE+"/browse/changesets?open=true\">"+Main.OSM_WEBSITE+"/browse/changesets?open=true</a></li>" 075 + "<li><a href=\"http://api.openstreetmap.org/api/0.6/changesets?open=true\">http://api.openstreetmap.org/api/0.6/changesets?open=true</a></li>" 076 + "</ul>" 077 + tr("Note that changeset queries are currently always submitted to ''{0}'', regardless of the " 078 + "host, port and path of the URL entered below.", OsmApi.getOsmApi().getBaseUrl()) 079 + "</body></html>" 080 ); 081 pnl.getEditorPane().addHyperlinkListener( 082 new HyperlinkListener() { 083 @Override 084 public void hyperlinkUpdate(HyperlinkEvent e) { 085 if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { 086 tfUrl.setText(e.getDescription()); 087 tfUrl.requestFocusInWindow(); 088 } 089 } 090 } 091 ); 092 return pnl; 093 } 094 095 protected void build() { 096 setLayout(new GridBagLayout()); 097 setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 098 099 GridBagConstraints gc = new GridBagConstraints(); 100 gc.weightx = 1.0; 101 gc.fill = GridBagConstraints.HORIZONTAL; 102 gc.insets = new Insets(0,0,10,0); 103 add(buildHelpPanel(),gc); 104 105 gc.gridy = 1; 106 gc.weightx = 1.0; 107 gc.fill = GridBagConstraints.HORIZONTAL; 108 add(buildURLPanel(),gc); 109 110 gc.gridy = 2; 111 gc.weightx = 1.0; 112 gc.weighty = 1.0; 113 gc.fill = GridBagConstraints.BOTH; 114 add(new JPanel(),gc); 115 116 } 117 public UrlBasedQueryPanel() { 118 build(); 119 } 120 121 protected boolean isValidChangesetQueryUrl(String text) { 122 return buildChangesetQuery(text) != null; 123 } 124 125 protected ChangesetQuery buildChangesetQuery(String text) { 126 URL url = null; 127 try { 128 url = new URL(text); 129 } catch(MalformedURLException e) { 130 return null; 131 } 132 String path = url.getPath(); 133 String query = url.getQuery(); 134 if (path == null || ! path.endsWith("/changesets")) return null; 135 136 try { 137 return ChangesetQuery.buildFromUrlQuery(query); 138 } catch(ChangesetQueryUrlException e) { 139 return null; 140 } 141 } 142 143 /** 144 * Replies the {@link ChangesetQuery} specified in this panel. null, if no valid changeset query 145 * is specified. 146 * 147 * @return the changeset query 148 */ 149 public ChangesetQuery buildChangesetQuery() { 150 String value = tfUrl.getText().trim(); 151 return buildChangesetQuery(value); 152 } 153 154 public void startUserInput() { 155 tfUrl.requestFocusInWindow(); 156 } 157 158 /** 159 * Validates text entered in the changeset query URL field on the fly 160 */ 161 class ChangetQueryUrlValidator implements DocumentListener { 162 protected String getCurrentFeedback() { 163 String fb = (String)lblValid.getClientProperty("valid"); 164 return fb == null ? "none" : fb; 165 } 166 protected void feedbackValid() { 167 if (getCurrentFeedback().equals("valid")) return; 168 lblValid.setIcon(ImageProvider.get("dialogs/changeset", "valid")); 169 lblValid.setToolTipText(""); 170 lblValid.putClientProperty("valid", "valid"); 171 } 172 173 protected void feedbackInvalid() { 174 if (getCurrentFeedback().equals("invalid")) return; 175 lblValid.setIcon(ImageProvider.get("warning-small")); 176 lblValid.setToolTipText(tr("This changeset query URL is invalid")); 177 lblValid.putClientProperty("valid", "invalid"); 178 } 179 180 protected void feedbackNone() { 181 lblValid.setIcon(null); 182 lblValid.putClientProperty("valid", "none"); 183 } 184 185 protected void validate() { 186 String value = tfUrl.getText(); 187 if (value.trim().isEmpty()) { 188 feedbackNone(); 189 return; 190 } 191 value = value.trim(); 192 if (isValidChangesetQueryUrl(value)) { 193 feedbackValid(); 194 } else { 195 feedbackInvalid(); 196 } 197 } 198 @Override 199 public void changedUpdate(DocumentEvent e) { 200 validate(); 201 } 202 203 @Override 204 public void insertUpdate(DocumentEvent e) { 205 validate(); 206 } 207 208 @Override 209 public void removeUpdate(DocumentEvent e) { 210 validate(); 211 } 212 } 213}