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

Last change on this file since 13761 was 13761, checked in by Don-vip, 6 years ago

see #16288 - replace similar i18n strings (removes 35 strings from Launchpad)

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