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