source: josm/trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java@ 10627

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

sonar - squid:S1166 - Exception handlers should preserve the original exceptions

  • Property svn:eol-style set to native
File size: 19.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.download;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.BorderLayout;
8import java.awt.Color;
9import java.awt.Component;
10import java.awt.Dimension;
11import java.awt.FlowLayout;
12import java.awt.Font;
13import java.awt.Graphics;
14import java.awt.GridBagLayout;
15import java.awt.event.ActionEvent;
16import java.awt.event.InputEvent;
17import java.awt.event.KeyEvent;
18import java.awt.event.WindowAdapter;
19import java.awt.event.WindowEvent;
20import java.util.ArrayList;
21import java.util.List;
22
23import javax.swing.AbstractAction;
24import javax.swing.JButton;
25import javax.swing.JCheckBox;
26import javax.swing.JComponent;
27import javax.swing.JDialog;
28import javax.swing.JLabel;
29import javax.swing.JOptionPane;
30import javax.swing.JPanel;
31import javax.swing.JTabbedPane;
32import javax.swing.KeyStroke;
33import javax.swing.event.ChangeListener;
34
35import org.openstreetmap.josm.Main;
36import org.openstreetmap.josm.actions.ExpertToggleAction;
37import org.openstreetmap.josm.data.Bounds;
38import org.openstreetmap.josm.gui.MapView;
39import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
40import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
41import org.openstreetmap.josm.gui.help.HelpUtil;
42import org.openstreetmap.josm.gui.util.GuiHelper;
43import org.openstreetmap.josm.io.OnlineResource;
44import org.openstreetmap.josm.plugins.PluginHandler;
45import org.openstreetmap.josm.tools.GBC;
46import org.openstreetmap.josm.tools.ImageProvider;
47import org.openstreetmap.josm.tools.InputMapUtils;
48import org.openstreetmap.josm.tools.OsmUrlToBounds;
49import org.openstreetmap.josm.tools.WindowGeometry;
50
51/**
52 * Dialog displayed to download OSM and/or GPS data from OSM server.
53 */
54public class DownloadDialog extends JDialog {
55 /** the unique instance of the download dialog */
56 private static DownloadDialog instance;
57
58 /**
59 * Replies the unique instance of the download dialog
60 *
61 * @return the unique instance of the download dialog
62 */
63 public static synchronized DownloadDialog getInstance() {
64 if (instance == null) {
65 instance = new DownloadDialog(Main.parent);
66 }
67 return instance;
68 }
69
70 protected SlippyMapChooser slippyMapChooser;
71 protected final transient List<DownloadSelection> downloadSelections = new ArrayList<>();
72 protected final JTabbedPane tpDownloadAreaSelectors = new JTabbedPane();
73 protected JCheckBox cbNewLayer;
74 protected JCheckBox cbStartup;
75 protected final JLabel sizeCheck = new JLabel();
76 protected transient Bounds currentBounds;
77 protected boolean canceled;
78
79 protected JCheckBox cbDownloadOsmData;
80 protected JCheckBox cbDownloadGpxData;
81 protected JCheckBox cbDownloadNotes;
82 /** the download action and button */
83 private final DownloadAction actDownload = new DownloadAction();
84 protected final JButton btnDownload = new JButton(actDownload);
85
86 private void makeCheckBoxRespondToEnter(JCheckBox cb) {
87 cb.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "doDownload");
88 cb.getActionMap().put("doDownload", actDownload);
89 }
90
91 protected final JPanel buildMainPanel() {
92 JPanel pnl = new JPanel(new GridBagLayout());
93
94 // size check depends on selected data source
95 final ChangeListener checkboxChangeListener = e -> updateSizeCheck();
96
97 // adding the download tasks
98 pnl.add(new JLabel(tr("Data Sources and Types:")), GBC.std().insets(5, 5, 1, 5));
99 cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
100 cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
101 cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
102 pnl.add(cbDownloadOsmData, GBC.std().insets(1, 5, 1, 5));
103 cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
104 cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
105 cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener);
106 pnl.add(cbDownloadGpxData, GBC.std().insets(5, 5, 1, 5));
107 cbDownloadNotes = new JCheckBox(tr("Notes"));
108 cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area."));
109 cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener);
110 pnl.add(cbDownloadNotes, GBC.eol().insets(50, 5, 1, 5));
111
112 // must be created before hook
113 slippyMapChooser = new SlippyMapChooser();
114
115 // hook for subclasses
116 buildMainPanelAboveDownloadSelections(pnl);
117
118 // predefined download selections
119 downloadSelections.add(slippyMapChooser);
120 downloadSelections.add(new BookmarkSelection());
121 downloadSelections.add(new BoundingBoxSelection());
122 downloadSelections.add(new PlaceSelection());
123 downloadSelections.add(new TileSelection());
124
125 // add selections from plugins
126 PluginHandler.addDownloadSelection(downloadSelections);
127
128 // now everybody may add their tab to the tabbed pane
129 // (not done right away to allow plugins to remove one of
130 // the default selectors!)
131 for (DownloadSelection s : downloadSelections) {
132 s.addGui(this);
133 }
134
135 pnl.add(tpDownloadAreaSelectors, GBC.eol().fill());
136
137 try {
138 tpDownloadAreaSelectors.setSelectedIndex(Main.pref.getInteger("download.tab", 0));
139 } catch (IndexOutOfBoundsException ex) {
140 Main.trace(ex);
141 Main.pref.putInteger("download.tab", 0);
142 }
143
144 Font labelFont = sizeCheck.getFont();
145 sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize()));
146
147 cbNewLayer = new JCheckBox(tr("Download as new layer"));
148 cbNewLayer.setToolTipText(tr("<html>Select to download data into a new data layer.<br>"
149 +"Unselect to download into the currently active data layer.</html>"));
150
151 cbStartup = new JCheckBox(tr("Open this dialog on startup"));
152 cbStartup.setToolTipText(
153 tr("<html>Autostart ''Download from OSM'' dialog every time JOSM is started.<br>" +
154 "You can open it manually from File menu or toolbar.</html>"));
155 cbStartup.addActionListener(e -> Main.pref.put("download.autorun", cbStartup.isSelected()));
156
157 pnl.add(cbNewLayer, GBC.std().anchor(GBC.WEST).insets(5, 5, 5, 5));
158 pnl.add(cbStartup, GBC.std().anchor(GBC.WEST).insets(15, 5, 5, 5));
159
160 pnl.add(sizeCheck, GBC.eol().anchor(GBC.EAST).insets(5, 5, 5, 2));
161
162 if (!ExpertToggleAction.isExpert()) {
163 JLabel infoLabel = new JLabel(
164 tr("Use left click&drag to select area, arrows or right mouse button to scroll map, wheel or +/- to zoom."));
165 pnl.add(infoLabel, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 0, 0));
166 }
167 return pnl;
168 }
169
170 /* This should not be necessary, but if not here, repaint is not always correct in SlippyMap! */
171 @Override
172 public void paint(Graphics g) {
173 tpDownloadAreaSelectors.getSelectedComponent().paint(g);
174 super.paint(g);
175 }
176
177 protected final JPanel buildButtonPanel() {
178 JPanel pnl = new JPanel(new FlowLayout());
179
180 // -- download button
181 pnl.add(btnDownload);
182 InputMapUtils.enableEnter(btnDownload);
183
184 makeCheckBoxRespondToEnter(cbDownloadGpxData);
185 makeCheckBoxRespondToEnter(cbDownloadOsmData);
186 makeCheckBoxRespondToEnter(cbDownloadNotes);
187 makeCheckBoxRespondToEnter(cbNewLayer);
188
189 // -- cancel button
190 JButton btnCancel;
191 CancelAction actCancel = new CancelAction();
192 btnCancel = new JButton(actCancel);
193 pnl.add(btnCancel);
194 InputMapUtils.enableEnter(btnCancel);
195
196 // -- cancel on ESC
197 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel");
198 getRootPane().getActionMap().put("cancel", actCancel);
199
200 // -- help button
201 JButton btnHelp = new JButton(new ContextSensitiveHelpAction(getRootPane().getClientProperty("help").toString()));
202 pnl.add(btnHelp);
203 InputMapUtils.enableEnter(btnHelp);
204
205 return pnl;
206 }
207
208 /**
209 * Constructs a new {@code DownloadDialog}.
210 * @param parent the parent component
211 */
212 public DownloadDialog(Component parent) {
213 this(parent, ht("/Action/Download"));
214 }
215
216 /**
217 * Constructs a new {@code DownloadDialog}.
218 * @param parent the parent component
219 * @param helpTopic the help topic to assign
220 */
221 public DownloadDialog(Component parent, String helpTopic) {
222 super(GuiHelper.getFrameForComponent(parent), tr("Download"), ModalityType.DOCUMENT_MODAL);
223 HelpUtil.setHelpContext(getRootPane(), helpTopic);
224 getContentPane().setLayout(new BorderLayout());
225 getContentPane().add(buildMainPanel(), BorderLayout.CENTER);
226 getContentPane().add(buildButtonPanel(), BorderLayout.SOUTH);
227
228 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
229 KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK), "checkClipboardContents");
230
231 getRootPane().getActionMap().put("checkClipboardContents", new AbstractAction() {
232 @Override
233 public void actionPerformed(ActionEvent e) {
234 String clip = ClipboardUtils.getClipboardStringContent();
235 if (clip == null) {
236 return;
237 }
238 Bounds b = OsmUrlToBounds.parse(clip);
239 if (b != null) {
240 boundingBoxChanged(new Bounds(b), null);
241 }
242 }
243 });
244 addWindowListener(new WindowEventHandler());
245 restoreSettings();
246 }
247
248 private void updateSizeCheck() {
249 boolean isAreaTooLarge = false;
250 if (currentBounds == null) {
251 sizeCheck.setText(tr("No area selected yet"));
252 sizeCheck.setForeground(Color.darkGray);
253 } else if (isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) {
254 // see max_note_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
255 isAreaTooLarge = currentBounds.getArea() > Main.pref.getDouble("osm-server.max-request-area-notes", 25);
256 } else {
257 // see max_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
258 isAreaTooLarge = currentBounds.getArea() > Main.pref.getDouble("osm-server.max-request-area", 0.25);
259 }
260 if (isAreaTooLarge) {
261 sizeCheck.setText(tr("Download area too large; will probably be rejected by server"));
262 sizeCheck.setForeground(Color.red);
263 } else {
264 sizeCheck.setText(tr("Download area ok, size probably acceptable to server"));
265 sizeCheck.setForeground(Color.darkGray);
266 }
267 }
268
269 /**
270 * Distributes a "bounding box changed" from one DownloadSelection
271 * object to the others, so they may update or clear their input fields.
272 * @param b new current bounds
273 *
274 * @param eventSource - the DownloadSelection object that fired this notification.
275 */
276 public void boundingBoxChanged(Bounds b, DownloadSelection eventSource) {
277 this.currentBounds = b;
278 for (DownloadSelection s : downloadSelections) {
279 if (s != eventSource) {
280 s.setDownloadArea(currentBounds);
281 }
282 }
283 updateSizeCheck();
284 }
285
286 /**
287 * Starts download for the given bounding box
288 * @param b bounding box to download
289 */
290 public void startDownload(Bounds b) {
291 this.currentBounds = b;
292 actDownload.run();
293 }
294
295 /**
296 * Replies true if the user selected to download OSM data
297 *
298 * @return true if the user selected to download OSM data
299 */
300 public boolean isDownloadOsmData() {
301 return cbDownloadOsmData.isSelected();
302 }
303
304 /**
305 * Replies true if the user selected to download GPX data
306 *
307 * @return true if the user selected to download GPX data
308 */
309 public boolean isDownloadGpxData() {
310 return cbDownloadGpxData.isSelected();
311 }
312
313 /**
314 * Replies true if user selected to download notes
315 *
316 * @return true if user selected to download notes
317 */
318 public boolean isDownloadNotes() {
319 return cbDownloadNotes.isSelected();
320 }
321
322 /**
323 * Replies true if the user requires to download into a new layer
324 *
325 * @return true if the user requires to download into a new layer
326 */
327 public boolean isNewLayerRequired() {
328 return cbNewLayer.isSelected();
329 }
330
331 /**
332 * Adds a new download area selector to the download dialog
333 *
334 * @param selector the download are selector
335 * @param displayName the display name of the selector
336 */
337 public void addDownloadAreaSelector(JPanel selector, String displayName) {
338 tpDownloadAreaSelectors.add(displayName, selector);
339 }
340
341 /**
342 * Refreshes the tile sources
343 * @since 6364
344 */
345 public final void refreshTileSources() {
346 if (slippyMapChooser != null) {
347 slippyMapChooser.refreshTileSources();
348 }
349 }
350
351 /**
352 * Remembers the current settings in the download dialog.
353 */
354 public void rememberSettings() {
355 Main.pref.put("download.tab", Integer.toString(tpDownloadAreaSelectors.getSelectedIndex()));
356 Main.pref.put("download.osm", cbDownloadOsmData.isSelected());
357 Main.pref.put("download.gps", cbDownloadGpxData.isSelected());
358 Main.pref.put("download.notes", cbDownloadNotes.isSelected());
359 Main.pref.put("download.newlayer", cbNewLayer.isSelected());
360 if (currentBounds != null) {
361 Main.pref.put("osm-download.bounds", currentBounds.encodeAsString(";"));
362 }
363 }
364
365 /**
366 * Restores the previous settings in the download dialog.
367 */
368 public void restoreSettings() {
369 cbDownloadOsmData.setSelected(Main.pref.getBoolean("download.osm", true));
370 cbDownloadGpxData.setSelected(Main.pref.getBoolean("download.gps", false));
371 cbDownloadNotes.setSelected(Main.pref.getBoolean("download.notes", false));
372 cbNewLayer.setSelected(Main.pref.getBoolean("download.newlayer", false));
373 cbStartup.setSelected(isAutorunEnabled());
374 int idx = Main.pref.getInteger("download.tab", 0);
375 if (idx < 0 || idx > tpDownloadAreaSelectors.getTabCount()) {
376 idx = 0;
377 }
378 tpDownloadAreaSelectors.setSelectedIndex(idx);
379
380 if (Main.isDisplayingMapView()) {
381 MapView mv = Main.map.mapView;
382 currentBounds = new Bounds(
383 mv.getLatLon(0, mv.getHeight()),
384 mv.getLatLon(mv.getWidth(), 0)
385 );
386 boundingBoxChanged(currentBounds, null);
387 } else {
388 Bounds bounds = getSavedDownloadBounds();
389 if (bounds != null) {
390 currentBounds = bounds;
391 boundingBoxChanged(currentBounds, null);
392 }
393 }
394 }
395
396 /**
397 * Returns the previously saved bounding box from preferences.
398 * @return The bounding box saved in preferences if any, {@code null} otherwise
399 * @since 6509
400 */
401 public static Bounds getSavedDownloadBounds() {
402 String value = Main.pref.get("osm-download.bounds");
403 if (!value.isEmpty()) {
404 try {
405 return new Bounds(value, ";");
406 } catch (IllegalArgumentException e) {
407 Main.warn(e);
408 }
409 }
410 return null;
411 }
412
413 /**
414 * Determines if the dialog autorun is enabled in preferences.
415 * @return {@code true} if the download dialog must be open at startup, {@code false} otherwise
416 */
417 public static boolean isAutorunEnabled() {
418 return Main.pref.getBoolean("download.autorun", false);
419 }
420
421 /**
422 * Automatically opens the download dialog, if autorun is enabled.
423 * @see #isAutorunEnabled
424 */
425 public static void autostartIfNeeded() {
426 if (isAutorunEnabled()) {
427 Main.main.menu.download.actionPerformed(null);
428 }
429 }
430
431 /**
432 * Replies the currently selected download area.
433 * @return the currently selected download area. May be {@code null}, if no download area is selected yet.
434 */
435 public Bounds getSelectedDownloadArea() {
436 return currentBounds;
437 }
438
439 @Override
440 public void setVisible(boolean visible) {
441 if (visible) {
442 new WindowGeometry(
443 getClass().getName() + ".geometry",
444 WindowGeometry.centerInWindow(
445 getParent(),
446 new Dimension(1000, 600)
447 )
448 ).applySafe(this);
449 } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
450 new WindowGeometry(this).remember(getClass().getName() + ".geometry");
451 }
452 super.setVisible(visible);
453 }
454
455 /**
456 * Replies true if the dialog was canceled
457 *
458 * @return true if the dialog was canceled
459 */
460 public boolean isCanceled() {
461 return canceled;
462 }
463
464 protected void setCanceled(boolean canceled) {
465 this.canceled = canceled;
466 }
467
468 protected void buildMainPanelAboveDownloadSelections(JPanel pnl) {
469 // Do nothing
470 }
471
472 class CancelAction extends AbstractAction {
473 CancelAction() {
474 putValue(NAME, tr("Cancel"));
475 new ImageProvider("cancel").getResource().attachImageIcon(this);
476 putValue(SHORT_DESCRIPTION, tr("Click to close the dialog and to abort downloading"));
477 }
478
479 public void run() {
480 setCanceled(true);
481 setVisible(false);
482 }
483
484 @Override
485 public void actionPerformed(ActionEvent e) {
486 run();
487 }
488 }
489
490 class DownloadAction extends AbstractAction {
491 DownloadAction() {
492 putValue(NAME, tr("Download"));
493 new ImageProvider("download").getResource().attachImageIcon(this);
494 putValue(SHORT_DESCRIPTION, tr("Click to download the currently selected area"));
495 setEnabled(!Main.isOffline(OnlineResource.OSM_API));
496 }
497
498 public void run() {
499 if (currentBounds == null) {
500 JOptionPane.showMessageDialog(
501 DownloadDialog.this,
502 tr("Please select a download area first."),
503 tr("Error"),
504 JOptionPane.ERROR_MESSAGE
505 );
506 return;
507 }
508 if (!isDownloadOsmData() && !isDownloadGpxData() && !isDownloadNotes()) {
509 JOptionPane.showMessageDialog(
510 DownloadDialog.this,
511 tr("<html>Neither <strong>{0}</strong> nor <strong>{1}</strong> nor <strong>{2}</strong> is enabled.<br>"
512 + "Please choose to either download OSM data, or GPX data, or Notes, or all.</html>",
513 cbDownloadOsmData.getText(),
514 cbDownloadGpxData.getText(),
515 cbDownloadNotes.getText()
516 ),
517 tr("Error"),
518 JOptionPane.ERROR_MESSAGE
519 );
520 return;
521 }
522 setCanceled(false);
523 setVisible(false);
524 }
525
526 @Override
527 public void actionPerformed(ActionEvent e) {
528 run();
529 }
530 }
531
532 class WindowEventHandler extends WindowAdapter {
533 @Override
534 public void windowClosing(WindowEvent e) {
535 new CancelAction().run();
536 }
537
538 @Override
539 public void windowActivated(WindowEvent e) {
540 btnDownload.requestFocusInWindow();
541 }
542 }
543}
Note: See TracBrowser for help on using the repository browser.