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

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

See #13309: Make download dialog use preferences interface.

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