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

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

see #15182 - deprecate Main.main.menu. Replacement: gui.MainApplication.getMenu()

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