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

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

replaced calls to JOptionPane.* by calls to OptionPaneUtil.*

File size: 12.8 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 JOptionPane.showMessageDialog(Main.parent,tr("Cannot read place search results from server"));
193 x.printStackTrace();
194 }
195 component.setCursor(oldCursor);
196 }
197 };
198 new Thread(r).start();
199 }
200
201 /**
202 * Adds a new tab to the download dialog in JOSM.
203 *
204 * This method is, for all intents and purposes, the constructor for this class.
205 */
206 public void addGui(final DownloadDialog gui) {
207 JPanel panel = new JPanel();
208 panel.setLayout(new GridBagLayout());
209
210 // this is manually tuned so that it looks nice on a GNOME
211 // desktop - maybe needs some cross platform proofing.
212 panel.add(new JLabel(tr("Enter a place name to search for:")), GBC.eol().insets(5, 5, 5, 5));
213 panel.add(searchTerm, GBC.std().fill(GBC.HORIZONTAL).insets(5, 0, 5, 4));
214 panel.add(submitSearch, GBC.eol().insets(5, 0, 5, 5));
215 Dimension btnSize = submitSearch.getPreferredSize();
216 btnSize.setSize(btnSize.width, btnSize.height * 0.8);
217 submitSearch.setPreferredSize(btnSize);
218
219 GBC c = GBC.std().fill().insets(5, 0, 5, 5);
220 c.gridwidth = 2;
221 JScrollPane scrollPane = new JScrollPane(searchResultDisplay);
222 scrollPane.setPreferredSize(new Dimension(200,200));
223 panel.add(scrollPane, c);
224 gui.tabpane.add(panel, tr("Places"));
225
226 scrollPane.setPreferredSize(scrollPane.getPreferredSize());
227
228 // when the button is clicked
229 submitSearch.addActionListener(new ActionListener() {
230 public void actionPerformed(ActionEvent e) {
231 queryServer(gui);
232 }
233 });
234
235 searchTerm.addActionListener(new ActionListener() {
236 public void actionPerformed(ActionEvent e) {
237 queryServer(gui);
238 }
239 });
240
241 searchResults.addColumn(tr("name"));
242 searchResults.addColumn(tr("type"));
243 searchResults.addColumn(tr("near"));
244 searchResults.addColumn(tr("zoom"));
245
246 // TODO - this is probably not the coolest way to set relative sizes?
247 searchResultDisplay.getColumn(tr("name")).setPreferredWidth(200);
248 searchResultDisplay.getColumn(tr("type")).setPreferredWidth(100);
249 searchResultDisplay.getColumn(tr("near")).setPreferredWidth(100);
250 searchResultDisplay.getColumn(tr("zoom")).setPreferredWidth(50);
251 searchResultDisplay.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
252
253 // display search results in a table. for simplicity, the table contains
254 // the same SearchResult object in each of the four columns, but it is rendered
255 // differently depending on the column.
256 searchResultDisplay.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
257 @Override public Component getTableCellRendererComponent(JTable table, Object value,
258 boolean isSelected, boolean hasFocus, int row, int column) {
259 super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
260 if (value != null) {
261 SearchResult sr = (SearchResult) value;
262 switch(column) {
263 case 0:
264 setText(sr.name);
265 break;
266 case 1:
267 setText(sr.type);
268 break;
269 case 2:
270 setText(sr.nearestPlace);
271 break;
272 case 3:
273 setText(Integer.toString(sr.zoom));
274 break;
275 }
276 setToolTipText("<html>"+((SearchResult)value).description+"</html>");
277 }
278 return this;
279 }
280 });
281
282 // if item is selected in list, notify dialog
283 searchResultDisplay.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
284 public void valueChanged(ListSelectionEvent lse) {
285 if (lse.getValueIsAdjusting()) return;
286 SearchResult r = null;
287 try
288 {
289 r = (SearchResult) searchResults.getValueAt(lse.getFirstIndex(), 0);
290 }
291 catch (Exception x)
292 {
293 // Ignore
294 }
295 if (r != null)
296 {
297 double size = 180.0 / Math.pow(2, r.zoom);
298 gui.minlat = r.lat - size / 2;
299 gui.maxlat = r.lat + size / 2;
300 gui.minlon = r.lon - size;
301 gui.maxlon = r.lon + size;
302 updatingSelf = true;
303 gui.boundingBoxChanged(null);
304 updatingSelf = false;
305 }
306 }
307 });
308
309 // TODO - we'd like to finish the download dialog upon double-click but
310 // don't know how to bypass the JOptionPane in which the whole thing is
311 // displayed.
312 searchResultDisplay.addMouseListener(new MouseAdapter() {
313 @Override public void mouseClicked(MouseEvent e) {
314 if (e.getClickCount() > 1) {
315 if (searchResultDisplay.getSelectionModel().getMinSelectionIndex() > -1) {
316 // add sensible action here.
317 }
318 }
319 }
320 });
321
322 }
323
324 // if bounding box selected on other tab, de-select item
325 public void boundingBoxChanged(DownloadDialog gui) {
326 if (!updatingSelf) {
327 searchResultDisplay.clearSelection();
328 }
329 }
330}
Note: See TracBrowser for help on using the repository browser.