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

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

see #11924 - fix remaining warnings about extended modifiers

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