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

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

sonar - squid:AssignmentInSubExpressionCheck - Assignments should not be made from within sub-expressions

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