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

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

see #15182 - deprecate Main.worker, replace it by gui.MainApplication.worker + code refactoring to make sure only editor packages use it

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