source: josm/trunk/src/org/openstreetmap/josm/gui/download/PlaceSelection.java@ 1865

Last change on this file since 1865 was 1857, checked in by Gubaer, 15 years ago

replaced JOptionDialog.show... by OptionPaneUtil.show....
improved relation editor

File size: 13.0 KB
Line 
1package org.openstreetmap.josm.gui.download;
2
3import static org.openstreetmap.josm.tools.I18n.tr;
4
5import java.awt.Component;
6import java.awt.Cursor;
7import java.awt.Dimension;
8import java.awt.GridBagLayout;
9import java.awt.event.ActionEvent;
10import java.awt.event.ActionListener;
11import java.awt.event.MouseAdapter;
12import java.awt.event.MouseEvent;
13import java.io.InputStream;
14import java.io.InputStreamReader;
15import java.net.HttpURLConnection;
16import java.net.URL;
17
18import javax.swing.JButton;
19import javax.swing.JComponent;
20import javax.swing.JLabel;
21import javax.swing.JOptionPane;
22import javax.swing.JPanel;
23import javax.swing.JScrollPane;
24import javax.swing.JTable;
25import javax.swing.JTextField;
26import javax.swing.ListSelectionModel;
27import javax.swing.event.ListSelectionEvent;
28import javax.swing.event.ListSelectionListener;
29import javax.swing.table.DefaultTableCellRenderer;
30import javax.swing.table.DefaultTableModel;
31import javax.xml.parsers.SAXParserFactory;
32
33import org.openstreetmap.josm.Main;
34import org.openstreetmap.josm.gui.OptionPaneUtil;
35import org.openstreetmap.josm.gui.download.DownloadDialog;
36import org.openstreetmap.josm.gui.download.DownloadSelection;
37import org.openstreetmap.josm.tools.GBC;
38import org.xml.sax.Attributes;
39import org.xml.sax.InputSource;
40import org.xml.sax.SAXException;
41import org.xml.sax.helpers.DefaultHandler;
42
43public class PlaceSelection implements DownloadSelection {
44
45 private JTextField searchTerm = new JTextField();
46 private JButton submitSearch = new JButton(tr("Search..."));
47 private DefaultTableModel searchResults = new DefaultTableModel() {
48 @Override public boolean isCellEditable(int row, int col) { return false; }
49 };
50 private JTable searchResultDisplay = new JTable(searchResults);
51 private boolean updatingSelf;
52
53 /**
54 * Data storage for search results.
55 */
56 class SearchResult
57 {
58 public String name;
59 public String type;
60 public String nearestPlace;
61 public String description;
62 public double lat;
63 public double lon;
64 public int zoom;
65 }
66
67 /**
68 * A very primitive parser for the name finder's output.
69 * Structure of xml described here: http://wiki.openstreetmap.org/index.php/Name_finder
70 *
71 */
72 private class Parser extends DefaultHandler
73 {
74 private SearchResult currentResult = null;
75 private StringBuffer description = null;
76 private int depth = 0;
77 /**
78 * Detect starting elements.
79 *
80 */
81 @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException
82 {
83 depth++;
84 try
85 {
86 if (qName.equals("searchresults"))
87 {
88 searchResults.setRowCount(0);
89 }
90 else if (qName.equals("named") && (depth == 2))
91 {
92 currentResult = new PlaceSelection.SearchResult();
93 currentResult.name = atts.getValue("name");
94 currentResult.type = atts.getValue("info");
95 currentResult.lat = Double.parseDouble(atts.getValue("lat"));
96 currentResult.lon = Double.parseDouble(atts.getValue("lon"));
97 currentResult.zoom = Integer.parseInt(atts.getValue("zoom"));
98 searchResults.addRow(new Object[] { currentResult, currentResult, currentResult, currentResult });
99 }
100 else if (qName.equals("description") && (depth == 3))
101 {
102 description = new StringBuffer();
103 }
104 else if (qName.equals("named") && (depth == 4))
105 {
106 // this is a "named" place in the nearest places list.
107 String info = atts.getValue("info");
108 if ("city".equals(info) || "town".equals(info) || "village".equals(info)) {
109 currentResult.nearestPlace = atts.getValue("name");
110 }
111 }
112 }
113 catch (NumberFormatException x)
114 {
115 x.printStackTrace(); // SAXException does not chain correctly
116 throw new SAXException(x.getMessage(), x);
117 }
118 catch (NullPointerException x)
119 {
120 x.printStackTrace(); // SAXException does not chain correctly
121 throw new SAXException(tr("Null pointer exception, possibly some missing tags."), x);
122 }
123 }
124 /**
125 * Detect ending elements.
126 */
127 @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException
128 {
129
130 if (qName.equals("searchresults"))
131 {
132 }
133 else if (qName.equals("description") && description != null)
134 {
135 currentResult.description = description.toString();
136 description = null;
137 }
138 depth--;
139
140 }
141 /**
142 * Read characters for description.
143 */
144 @Override public void characters(char[] data, int start, int length) throws org.xml.sax.SAXException
145 {
146 if (description != null)
147 {
148 description.append(data, start, length);
149 }
150 }
151 }
152
153 /**
154 * This queries David Earl's server. Needless to say, stuff should be configurable, and
155 * error handling improved.
156 */
157 public void queryServer(final JComponent component)
158 {
159 final Cursor oldCursor = component.getCursor();
160
161 // had to put this in a thread as it wouldn't update the cursor properly before.
162 Runnable r = new Runnable() {
163 public void run() {
164 try
165 {
166 String searchtext = searchTerm.getText();
167 if(searchtext.length()==0)
168 {
169 OptionPaneUtil.showMessageDialog(
170 Main.parent,
171 tr("Please enter a search string"),
172 tr("Information"),
173 JOptionPane.INFORMATION_MESSAGE
174 );
175 }
176 else
177 {
178 component.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
179 component.repaint();
180 URL url = new URL("http://gazetteer.openstreetmap.org/namefinder/search.xml?find="
181 +java.net.URLEncoder.encode(searchTerm.getText(), "UTF-8"));
182 HttpURLConnection activeConnection = (HttpURLConnection)url.openConnection();
183 //System.out.println("got return: "+activeConnection.getResponseCode());
184 activeConnection.setConnectTimeout(15000);
185 InputStream inputStream = activeConnection.getInputStream();
186 InputSource inputSource = new InputSource(new InputStreamReader(inputStream, "UTF-8"));
187 SAXParserFactory.newInstance().newSAXParser().parse(inputSource, new Parser());
188 }
189 }
190 catch (Exception x)
191 {
192 x.printStackTrace();
193 OptionPaneUtil.showMessageDialog(
194 Main.parent,
195 tr("Cannot read place search results from server"),
196 tr("Error"),
197 JOptionPane.ERROR_MESSAGE
198 );
199 }
200 component.setCursor(oldCursor);
201 }
202 };
203 new Thread(r).start();
204 }
205
206 /**
207 * Adds a new tab to the download dialog in JOSM.
208 *
209 * This method is, for all intents and purposes, the constructor for this class.
210 */
211 public void addGui(final DownloadDialog gui) {
212 JPanel panel = new JPanel();
213 panel.setLayout(new GridBagLayout());
214
215 // this is manually tuned so that it looks nice on a GNOME
216 // desktop - maybe needs some cross platform proofing.
217 panel.add(new JLabel(tr("Enter a place name to search for:")), GBC.eol().insets(5, 5, 5, 5));
218 panel.add(searchTerm, GBC.std().fill(GBC.HORIZONTAL).insets(5, 0, 5, 4));
219 panel.add(submitSearch, GBC.eol().insets(5, 0, 5, 5));
220 Dimension btnSize = submitSearch.getPreferredSize();
221 btnSize.setSize(btnSize.width, btnSize.height * 0.8);
222 submitSearch.setPreferredSize(btnSize);
223
224 GBC c = GBC.std().fill().insets(5, 0, 5, 5);
225 c.gridwidth = 2;
226 JScrollPane scrollPane = new JScrollPane(searchResultDisplay);
227 scrollPane.setPreferredSize(new Dimension(200,200));
228 panel.add(scrollPane, c);
229 gui.tabpane.add(panel, tr("Places"));
230
231 scrollPane.setPreferredSize(scrollPane.getPreferredSize());
232
233 // when the button is clicked
234 submitSearch.addActionListener(new ActionListener() {
235 public void actionPerformed(ActionEvent e) {
236 queryServer(gui);
237 }
238 });
239
240 searchTerm.addActionListener(new ActionListener() {
241 public void actionPerformed(ActionEvent e) {
242 queryServer(gui);
243 }
244 });
245
246 searchResults.addColumn(tr("name"));
247 searchResults.addColumn(tr("type"));
248 searchResults.addColumn(tr("near"));
249 searchResults.addColumn(tr("zoom"));
250
251 // TODO - this is probably not the coolest way to set relative sizes?
252 searchResultDisplay.getColumn(tr("name")).setPreferredWidth(200);
253 searchResultDisplay.getColumn(tr("type")).setPreferredWidth(100);
254 searchResultDisplay.getColumn(tr("near")).setPreferredWidth(100);
255 searchResultDisplay.getColumn(tr("zoom")).setPreferredWidth(50);
256 searchResultDisplay.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
257
258 // display search results in a table. for simplicity, the table contains
259 // the same SearchResult object in each of the four columns, but it is rendered
260 // differently depending on the column.
261 searchResultDisplay.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
262 @Override public Component getTableCellRendererComponent(JTable table, Object value,
263 boolean isSelected, boolean hasFocus, int row, int column) {
264 super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
265 if (value != null) {
266 SearchResult sr = (SearchResult) value;
267 switch(column) {
268 case 0:
269 setText(sr.name);
270 break;
271 case 1:
272 setText(sr.type);
273 break;
274 case 2:
275 setText(sr.nearestPlace);
276 break;
277 case 3:
278 setText(Integer.toString(sr.zoom));
279 break;
280 }
281 setToolTipText("<html>"+((SearchResult)value).description+"</html>");
282 }
283 return this;
284 }
285 });
286
287 // if item is selected in list, notify dialog
288 searchResultDisplay.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
289 public void valueChanged(ListSelectionEvent lse) {
290 if (lse.getValueIsAdjusting()) return;
291 SearchResult r = null;
292 try
293 {
294 r = (SearchResult) searchResults.getValueAt(lse.getFirstIndex(), 0);
295 }
296 catch (Exception x)
297 {
298 // Ignore
299 }
300 if (r != null)
301 {
302 double size = 180.0 / Math.pow(2, r.zoom);
303 gui.minlat = r.lat - size / 2;
304 gui.maxlat = r.lat + size / 2;
305 gui.minlon = r.lon - size;
306 gui.maxlon = r.lon + size;
307 updatingSelf = true;
308 gui.boundingBoxChanged(null);
309 updatingSelf = false;
310 }
311 }
312 });
313
314 // TODO - we'd like to finish the download dialog upon double-click but
315 // don't know how to bypass the JOptionPane in which the whole thing is
316 // displayed.
317 searchResultDisplay.addMouseListener(new MouseAdapter() {
318 @Override public void mouseClicked(MouseEvent e) {
319 if (e.getClickCount() > 1) {
320 if (searchResultDisplay.getSelectionModel().getMinSelectionIndex() > -1) {
321 // add sensible action here.
322 }
323 }
324 }
325 });
326
327 }
328
329 // if bounding box selected on other tab, de-select item
330 public void boundingBoxChanged(DownloadDialog gui) {
331 if (!updatingSelf) {
332 searchResultDisplay.clearSelection();
333 }
334 }
335}
Note: See TracBrowser for help on using the repository browser.