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

Last change on this file since 12575 was 12575, checked in by michael2402, 7 years ago

See #15057: Fix checkstye and sonar issues.

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