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

Last change on this file since 11717 was 11009, checked in by Don-vip, 8 years ago

fix recent sonar/checkstyle issues

  • Property svn:eol-style set to native
File size: 16.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.download;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
8import java.awt.Dimension;
9import java.awt.GridBagLayout;
10import java.awt.GridLayout;
11import java.awt.event.ActionEvent;
12import java.awt.event.MouseAdapter;
13import java.awt.event.MouseEvent;
14import java.io.IOException;
15import java.io.Reader;
16import java.net.URL;
17import java.text.DecimalFormat;
18import java.util.ArrayList;
19import java.util.Collections;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.StringTokenizer;
23
24import javax.swing.AbstractAction;
25import javax.swing.BorderFactory;
26import javax.swing.DefaultListSelectionModel;
27import javax.swing.JButton;
28import javax.swing.JLabel;
29import javax.swing.JOptionPane;
30import javax.swing.JPanel;
31import javax.swing.JScrollPane;
32import javax.swing.JTable;
33import javax.swing.ListSelectionModel;
34import javax.swing.UIManager;
35import javax.swing.event.DocumentEvent;
36import javax.swing.event.DocumentListener;
37import javax.swing.event.ListSelectionEvent;
38import javax.swing.event.ListSelectionListener;
39import javax.swing.table.DefaultTableColumnModel;
40import javax.swing.table.DefaultTableModel;
41import javax.swing.table.TableCellRenderer;
42import javax.swing.table.TableColumn;
43import javax.xml.parsers.ParserConfigurationException;
44
45import org.openstreetmap.josm.Main;
46import org.openstreetmap.josm.data.Bounds;
47import org.openstreetmap.josm.gui.ExceptionDialogUtil;
48import org.openstreetmap.josm.gui.HelpAwareOptionPane;
49import org.openstreetmap.josm.gui.PleaseWaitRunnable;
50import org.openstreetmap.josm.gui.util.GuiHelper;
51import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
52import org.openstreetmap.josm.gui.widgets.JosmComboBox;
53import org.openstreetmap.josm.io.NameFinder;
54import org.openstreetmap.josm.io.NameFinder.SearchResult;
55import org.openstreetmap.josm.io.OsmTransferException;
56import org.openstreetmap.josm.tools.GBC;
57import org.openstreetmap.josm.tools.HttpClient;
58import org.openstreetmap.josm.tools.ImageProvider;
59import org.openstreetmap.josm.tools.Utils;
60import org.xml.sax.SAXException;
61import org.xml.sax.SAXParseException;
62
63/**
64 * Place selector.
65 * @since 1329
66 */
67public class PlaceSelection implements DownloadSelection {
68 private static final String HISTORY_KEY = "download.places.history";
69
70 private HistoryComboBox cbSearchExpression;
71 private NamedResultTableModel model;
72 private NamedResultTableColumnModel columnmodel;
73 private JTable tblSearchResults;
74 private DownloadDialog parent;
75 private static final Server[] SERVERS = new Server[] {
76 new Server("Nominatim", NameFinder.NOMINATIM_URL, tr("Class Type"), tr("Bounds"))
77 };
78 private final JosmComboBox<Server> server = new JosmComboBox<>(SERVERS);
79
80 private static class Server {
81 public final String name;
82 public final String url;
83 public final String thirdcol;
84 public final String fourthcol;
85
86 Server(String n, String u, String t, String f) {
87 name = n;
88 url = u;
89 thirdcol = t;
90 fourthcol = f;
91 }
92
93 @Override
94 public String toString() {
95 return name;
96 }
97 }
98
99 protected JPanel buildSearchPanel() {
100 JPanel lpanel = new JPanel(new GridLayout(2, 2));
101 JPanel panel = new JPanel(new GridBagLayout());
102
103 lpanel.add(new JLabel(tr("Choose the server for searching:")));
104 lpanel.add(server);
105 String s = Main.pref.get("namefinder.server", SERVERS[0].name);
106 for (int i = 0; i < SERVERS.length; ++i) {
107 if (SERVERS[i].name.equals(s)) {
108 server.setSelectedIndex(i);
109 }
110 }
111 lpanel.add(new JLabel(tr("Enter a place name to search for:")));
112
113 cbSearchExpression = new HistoryComboBox();
114 cbSearchExpression.setToolTipText(tr("Enter a place name to search for"));
115 List<String> cmtHistory = new LinkedList<>(Main.pref.getCollection(HISTORY_KEY, new LinkedList<String>()));
116 Collections.reverse(cmtHistory);
117 cbSearchExpression.setPossibleItems(cmtHistory);
118 lpanel.add(cbSearchExpression);
119
120 panel.add(lpanel, GBC.std().fill(GBC.HORIZONTAL).insets(5, 5, 0, 5));
121 SearchAction searchAction = new SearchAction();
122 JButton btnSearch = new JButton(searchAction);
123 cbSearchExpression.getEditorComponent().getDocument().addDocumentListener(searchAction);
124 cbSearchExpression.getEditorComponent().addActionListener(searchAction);
125
126 panel.add(btnSearch, GBC.eol().insets(5, 5, 0, 5));
127
128 return panel;
129 }
130
131 /**
132 * Adds a new tab to the download dialog in JOSM.
133 *
134 * This method is, for all intents and purposes, the constructor for this class.
135 */
136 @Override
137 public void addGui(final DownloadDialog gui) {
138 JPanel panel = new JPanel(new BorderLayout());
139 panel.add(buildSearchPanel(), BorderLayout.NORTH);
140
141 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
142 model = new NamedResultTableModel(selectionModel);
143 columnmodel = new NamedResultTableColumnModel();
144 tblSearchResults = new JTable(model, columnmodel);
145 tblSearchResults.setSelectionModel(selectionModel);
146 JScrollPane scrollPane = new JScrollPane(tblSearchResults);
147 scrollPane.setPreferredSize(new Dimension(200, 200));
148 panel.add(scrollPane, BorderLayout.CENTER);
149
150 if (gui != null)
151 gui.addDownloadAreaSelector(panel, tr("Areas around places"));
152
153 scrollPane.setPreferredSize(scrollPane.getPreferredSize());
154 tblSearchResults.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
155 tblSearchResults.getSelectionModel().addListSelectionListener(new ListSelectionHandler());
156 tblSearchResults.addMouseListener(new MouseAdapter() {
157 @Override
158 public void mouseClicked(MouseEvent e) {
159 if (e.getClickCount() > 1) {
160 SearchResult sr = model.getSelectedSearchResult();
161 if (sr != null) {
162 parent.startDownload(sr.getDownloadArea());
163 }
164 }
165 }
166 });
167 parent = gui;
168 }
169
170 @Override
171 public void setDownloadArea(Bounds area) {
172 tblSearchResults.clearSelection();
173 }
174
175 class SearchAction extends AbstractAction implements DocumentListener {
176
177 SearchAction() {
178 putValue(NAME, tr("Search ..."));
179 putValue(SMALL_ICON, ImageProvider.get("dialogs", "search"));
180 putValue(SHORT_DESCRIPTION, tr("Click to start searching for places"));
181 updateEnabledState();
182 }
183
184 @Override
185 public void actionPerformed(ActionEvent e) {
186 if (!isEnabled() || cbSearchExpression.getText().trim().isEmpty())
187 return;
188 cbSearchExpression.addCurrentItemToHistory();
189 Main.pref.putCollection(HISTORY_KEY, cbSearchExpression.getHistory());
190 NameQueryTask task = new NameQueryTask(cbSearchExpression.getText());
191 Main.worker.submit(task);
192 }
193
194 protected final void updateEnabledState() {
195 setEnabled(!cbSearchExpression.getText().trim().isEmpty());
196 }
197
198 @Override
199 public void changedUpdate(DocumentEvent e) {
200 updateEnabledState();
201 }
202
203 @Override
204 public void insertUpdate(DocumentEvent e) {
205 updateEnabledState();
206 }
207
208 @Override
209 public void removeUpdate(DocumentEvent e) {
210 updateEnabledState();
211 }
212 }
213
214 class NameQueryTask extends PleaseWaitRunnable {
215
216 private final String searchExpression;
217 private HttpClient connection;
218 private List<SearchResult> data;
219 private boolean canceled;
220 private final Server useserver;
221 private Exception lastException;
222
223 NameQueryTask(String searchExpression) {
224 super(tr("Querying name server"), false /* don't ignore exceptions */);
225 this.searchExpression = searchExpression;
226 useserver = (Server) server.getSelectedItem();
227 Main.pref.put("namefinder.server", useserver.name);
228 }
229
230 @Override
231 protected void cancel() {
232 this.canceled = true;
233 synchronized (this) {
234 if (connection != null) {
235 connection.disconnect();
236 }
237 }
238 }
239
240 @Override
241 protected void finish() {
242 if (canceled)
243 return;
244 if (lastException != null) {
245 ExceptionDialogUtil.explainException(lastException);
246 return;
247 }
248 columnmodel.setHeadlines(useserver.thirdcol, useserver.fourthcol);
249 model.setData(this.data);
250 }
251
252 @Override
253 protected void realRun() throws SAXException, IOException, OsmTransferException {
254 String urlString = useserver.url+Utils.encodeUrl(searchExpression);
255
256 try {
257 getProgressMonitor().indeterminateSubTask(tr("Querying name server ..."));
258 URL url = new URL(urlString);
259 synchronized (this) {
260 connection = HttpClient.create(url);
261 connection.connect();
262 }
263 try (Reader reader = connection.getResponse().getContentReader()) {
264 data = NameFinder.parseSearchResults(reader);
265 }
266 } catch (SAXParseException e) {
267 if (!canceled) {
268 // Nominatim sometimes returns garbage, see #5934, #10643
269 Main.warn(e, tr("Error occured with query ''{0}'': ''{1}''", urlString, e.getMessage()));
270 GuiHelper.runInEDTAndWait(() -> HelpAwareOptionPane.showOptionDialog(
271 Main.parent,
272 tr("Name server returned invalid data. Please try again."),
273 tr("Bad response"),
274 JOptionPane.WARNING_MESSAGE, null
275 ));
276 }
277 } catch (IOException | ParserConfigurationException e) {
278 if (!canceled) {
279 OsmTransferException ex = new OsmTransferException(e);
280 ex.setUrl(urlString);
281 lastException = ex;
282 }
283 }
284 }
285 }
286
287 static class NamedResultTableModel extends DefaultTableModel {
288 private transient List<SearchResult> data;
289 private final transient ListSelectionModel selectionModel;
290
291 NamedResultTableModel(ListSelectionModel selectionModel) {
292 data = new ArrayList<>();
293 this.selectionModel = selectionModel;
294 }
295
296 @Override
297 public int getRowCount() {
298 return data != null ? data.size() : 0;
299 }
300
301 @Override
302 public Object getValueAt(int row, int column) {
303 return data != null ? data.get(row) : null;
304 }
305
306 public void setData(List<SearchResult> data) {
307 if (data == null) {
308 this.data.clear();
309 } else {
310 this.data = new ArrayList<>(data);
311 }
312 fireTableDataChanged();
313 }
314
315 @Override
316 public boolean isCellEditable(int row, int column) {
317 return false;
318 }
319
320 public SearchResult getSelectedSearchResult() {
321 if (selectionModel.getMinSelectionIndex() < 0)
322 return null;
323 return data.get(selectionModel.getMinSelectionIndex());
324 }
325 }
326
327 static class NamedResultTableColumnModel extends DefaultTableColumnModel {
328 private TableColumn col3;
329 private TableColumn col4;
330
331 NamedResultTableColumnModel() {
332 createColumns();
333 }
334
335 protected final void createColumns() {
336 TableColumn col;
337 NamedResultCellRenderer renderer = new NamedResultCellRenderer();
338
339 // column 0 - Name
340 col = new TableColumn(0);
341 col.setHeaderValue(tr("Name"));
342 col.setResizable(true);
343 col.setPreferredWidth(200);
344 col.setCellRenderer(renderer);
345 addColumn(col);
346
347 // column 1 - Version
348 col = new TableColumn(1);
349 col.setHeaderValue(tr("Type"));
350 col.setResizable(true);
351 col.setPreferredWidth(100);
352 col.setCellRenderer(renderer);
353 addColumn(col);
354
355 // column 2 - Near
356 col3 = new TableColumn(2);
357 col3.setHeaderValue(SERVERS[0].thirdcol);
358 col3.setResizable(true);
359 col3.setPreferredWidth(100);
360 col3.setCellRenderer(renderer);
361 addColumn(col3);
362
363 // column 3 - Zoom
364 col4 = new TableColumn(3);
365 col4.setHeaderValue(SERVERS[0].fourthcol);
366 col4.setResizable(true);
367 col4.setPreferredWidth(50);
368 col4.setCellRenderer(renderer);
369 addColumn(col4);
370 }
371
372 public void setHeadlines(String third, String fourth) {
373 col3.setHeaderValue(third);
374 col4.setHeaderValue(fourth);
375 fireColumnMarginChanged();
376 }
377 }
378
379 class ListSelectionHandler implements ListSelectionListener {
380 @Override
381 public void valueChanged(ListSelectionEvent lse) {
382 SearchResult r = model.getSelectedSearchResult();
383 if (r != null) {
384 parent.boundingBoxChanged(r.getDownloadArea(), PlaceSelection.this);
385 }
386 }
387 }
388
389 static class NamedResultCellRenderer extends JLabel implements TableCellRenderer {
390
391 /**
392 * Constructs a new {@code NamedResultCellRenderer}.
393 */
394 NamedResultCellRenderer() {
395 setOpaque(true);
396 setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
397 }
398
399 protected void reset() {
400 setText("");
401 setIcon(null);
402 }
403
404 protected void renderColor(boolean selected) {
405 if (selected) {
406 setForeground(UIManager.getColor("Table.selectionForeground"));
407 setBackground(UIManager.getColor("Table.selectionBackground"));
408 } else {
409 setForeground(UIManager.getColor("Table.foreground"));
410 setBackground(UIManager.getColor("Table.background"));
411 }
412 }
413
414 protected String lineWrapDescription(String description) {
415 StringBuilder ret = new StringBuilder();
416 StringBuilder line = new StringBuilder();
417 StringTokenizer tok = new StringTokenizer(description, " ");
418 while (tok.hasMoreElements()) {
419 String t = tok.nextToken();
420 if (line.length() == 0) {
421 line.append(t);
422 } else if (line.length() < 80) {
423 line.append(' ').append(t);
424 } else {
425 line.append(' ').append(t).append("<br>");
426 ret.append(line);
427 line = new StringBuilder();
428 }
429 }
430 ret.insert(0, "<html>");
431 ret.append("</html>");
432 return ret.toString();
433 }
434
435 @Override
436 public Component getTableCellRendererComponent(JTable table, Object value,
437 boolean isSelected, boolean hasFocus, int row, int column) {
438
439 reset();
440 renderColor(isSelected);
441
442 if (value == null)
443 return this;
444 SearchResult sr = (SearchResult) value;
445 switch(column) {
446 case 0:
447 setText(sr.getName());
448 break;
449 case 1:
450 setText(sr.getInfo());
451 break;
452 case 2:
453 setText(sr.getNearestPlace());
454 break;
455 case 3:
456 if (sr.getBounds() != null) {
457 setText(sr.getBounds().toShortString(new DecimalFormat("0.000")));
458 } else {
459 setText(sr.getZoom() != 0 ? Integer.toString(sr.getZoom()) : tr("unknown"));
460 }
461 break;
462 default: // Do nothing
463 }
464 setToolTipText(lineWrapDescription(sr.getDescription()));
465 return this;
466 }
467 }
468}
Note: See TracBrowser for help on using the repository browser.