Index: trunk/src/org/openstreetmap/josm/actions/DownloadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/DownloadAction.java	(revision 12651)
+++ trunk/src/org/openstreetmap/josm/actions/DownloadAction.java	(revision 12652)
@@ -7,26 +7,6 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
 
-import javax.swing.JOptionPane;
-
-import org.openstreetmap.josm.actions.downloadtasks.AbstractDownloadTask;
-import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask;
-import org.openstreetmap.josm.actions.downloadtasks.DownloadNotesTask;
-import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
-import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
-import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.ProjectionBounds;
-import org.openstreetmap.josm.data.ViewportData;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.gui.download.DownloadDialog;
-import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.tools.Logging;
-import org.openstreetmap.josm.tools.Pair;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -45,6 +25,6 @@
      */
     public DownloadAction() {
-        super(tr("Download from OSM..."), "download", tr("Download map data from the OSM server."),
-              Shortcut.registerShortcut("file:download", tr("File: {0}", tr("Download from OSM...")), KeyEvent.VK_DOWN, Shortcut.CTRL_SHIFT),
+        super(tr("Download data"), "download", tr("Download map data from a server of your choice"),
+              Shortcut.registerShortcut("file:download", tr("File: {0}", tr("Download data")), KeyEvent.VK_DOWN, Shortcut.CTRL_SHIFT),
               true);
         putValue("help", ht("/Action/Download"));
@@ -56,81 +36,4 @@
         dialog.restoreSettings();
         dialog.setVisible(true);
-
-        if (dialog.isCanceled()) {
-            return;
-        }
-
-        dialog.rememberSettings();
-
-        Optional<Bounds> selectedArea = dialog.getSelectedDownloadArea();
-        if (!selectedArea.isPresent()) {
-            JOptionPane.showMessageDialog(
-                    dialog,
-                    tr("Please select a download area first."),
-                    tr("Error"),
-                    JOptionPane.ERROR_MESSAGE
-            );
-            return;
-        }
-
-        final Bounds area = selectedArea.get();
-        final boolean zoom = dialog.isZoomToDownloadedDataRequired();
-        final List<Pair<AbstractDownloadTask<?>, Future<?>>> tasks = new ArrayList<>();
-
-        if (dialog.isDownloadOsmData()) {
-            DownloadOsmTask task = new DownloadOsmTask();
-            task.setZoomAfterDownload(zoom && !dialog.isDownloadGpxData() && !dialog.isDownloadNotes());
-            Future<?> future = task.download(dialog.isNewLayerRequired(), area, null);
-            MainApplication.worker.submit(new PostDownloadHandler(task, future));
-            if (zoom) {
-                tasks.add(new Pair<>(task, future));
-            }
-        }
-
-        if (dialog.isDownloadGpxData()) {
-            DownloadGpsTask task = new DownloadGpsTask();
-            task.setZoomAfterDownload(zoom && !dialog.isDownloadOsmData() && !dialog.isDownloadNotes());
-            Future<?> future = task.download(dialog.isNewLayerRequired(), area, null);
-            MainApplication.worker.submit(new PostDownloadHandler(task, future));
-            if (zoom) {
-                tasks.add(new Pair<>(task, future));
-            }
-        }
-
-        if (dialog.isDownloadNotes()) {
-            DownloadNotesTask task = new DownloadNotesTask();
-            task.setZoomAfterDownload(zoom && !dialog.isDownloadOsmData() && !dialog.isDownloadGpxData());
-            Future<?> future = task.download(false, area, null);
-            MainApplication.worker.submit(new PostDownloadHandler(task, future));
-            if (zoom) {
-                tasks.add(new Pair<>(task, future));
-            }
-        }
-
-        if (zoom && tasks.size() > 1) {
-            MainApplication.worker.submit(() -> {
-                ProjectionBounds bounds = null;
-                // Wait for completion of download jobs
-                for (Pair<AbstractDownloadTask<?>, Future<?>> p : tasks) {
-                    try {
-                        p.b.get();
-                        ProjectionBounds b = p.a.getDownloadProjectionBounds();
-                        if (bounds == null) {
-                            bounds = b;
-                        } else if (b != null) {
-                            bounds.extend(b);
-                        }
-                    } catch (InterruptedException | ExecutionException ex) {
-                        Logging.warn(ex);
-                    }
-                }
-                // Zoom to the larger download bounds
-                MapFrame map = MainApplication.getMap();
-                if (map != null && bounds != null) {
-                    final ProjectionBounds pb = bounds;
-                    GuiHelper.runInEDTAndWait(() -> map.mapView.zoomTo(new ViewportData(pb)));
-                }
-            });
-        }
     }
 }
Index: trunk/src/org/openstreetmap/josm/actions/OverpassDownloadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/OverpassDownloadAction.java	(revision 12651)
+++ 	(revision )
@@ -1,419 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.actions;
-
-import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.GridBagLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.FocusEvent;
-import java.awt.event.FocusListener;
-import java.awt.event.KeyEvent;
-import java.util.Collection;
-import java.util.Optional;
-import java.util.concurrent.Future;
-import java.util.function.Consumer;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.ActionMap;
-import javax.swing.JButton;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-import javax.swing.plaf.basic.BasicArrowButton;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
-import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
-import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.preferences.BooleanProperty;
-import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.download.DownloadDialog;
-import org.openstreetmap.josm.gui.download.OverpassQueryList;
-import org.openstreetmap.josm.gui.download.OverpassQueryWizardDialog;
-import org.openstreetmap.josm.gui.preferences.server.OverpassServerPreference;
-import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.gui.widgets.JosmTextArea;
-import org.openstreetmap.josm.io.OverpassDownloadReader;
-import org.openstreetmap.josm.tools.GBC;
-import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.Shortcut;
-
-/**
- * Download map data from Overpass API server.
- * @since 8684
- */
-public class OverpassDownloadAction extends JosmAction {
-
-    /**
-     * Constructs a new {@code OverpassDownloadAction}.
-     */
-    public OverpassDownloadAction() {
-        super(tr("Download from Overpass API ..."), "download-overpass", tr("Download map data from Overpass API server."),
-                // CHECKSTYLE.OFF: LineLength
-                Shortcut.registerShortcut("file:download-overpass", tr("File: {0}", tr("Download from Overpass API ...")), KeyEvent.VK_DOWN, Shortcut.ALT_SHIFT),
-                // CHECKSTYLE.ON: LineLength
-                true, "overpassdownload/download", true);
-        putValue("help", ht("/Action/OverpassDownload"));
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        OverpassDownloadDialog dialog = OverpassDownloadDialog.getInstance();
-        dialog.restoreSettings();
-        dialog.setVisible(true);
-
-        if (dialog.isCanceled()) {
-            return;
-        }
-
-        dialog.rememberSettings();
-        Optional<Bounds> selectedArea = dialog.getSelectedDownloadArea();
-        String overpassQuery = dialog.getRepairedOverpassQuery();
-
-        /*
-         * Absence of the selected area can be justified only if the overpass query
-         * is not restricted to bbox.
-         */
-        if (!selectedArea.isPresent() && overpassQuery.contains("{{bbox}}")) {
-            JOptionPane.showMessageDialog(
-                    dialog,
-                    tr("Please select a download area first."),
-                    tr("Error"),
-                    JOptionPane.ERROR_MESSAGE
-            );
-            return;
-        }
-
-        /*
-         * A callback that is passed to PostDownloadReporter that is called once the download task
-         * has finished. According to the number of errors happened, their type we decide whether we
-         * want to save the last query in OverpassQueryList.
-         */
-        Consumer<Collection<Object>> errorReporter = errors -> {
-
-            boolean onlyNoDataError = errors.size() == 1 &&
-                    errors.contains("No data found in this area.");
-
-            if (errors.isEmpty() || onlyNoDataError) {
-                dialog.saveHistoricItemOnSuccess(overpassQuery);
-            }
-        };
-
-        /*
-         * In order to support queries generated by the Overpass Turbo Query Wizard tool
-         * which do not require the area to be specified.
-         */
-        Bounds area = selectedArea.orElseGet(() -> new Bounds(0, 0, 0, 0));
-        DownloadOsmTask task = new DownloadOsmTask();
-        task.setZoomAfterDownload(dialog.isZoomToDownloadedDataRequired());
-        Future<?> future = task.download(
-                new OverpassDownloadReader(area, OverpassServerPreference.getOverpassServer(), overpassQuery),
-                dialog.isNewLayerRequired(), area, null);
-        MainApplication.worker.submit(new PostDownloadHandler(task, future, errorReporter));
-    }
-
-    private static final class DisableActionsFocusListener implements FocusListener {
-
-        private final ActionMap actionMap;
-
-        private DisableActionsFocusListener(ActionMap actionMap) {
-            this.actionMap = actionMap;
-        }
-
-        @Override
-        public void focusGained(FocusEvent e) {
-            enableActions(false);
-        }
-
-        @Override
-        public void focusLost(FocusEvent e) {
-            enableActions(true);
-        }
-
-        private void enableActions(boolean enabled) {
-            Object[] allKeys = actionMap.allKeys();
-            if (allKeys != null) {
-                for (Object key : allKeys) {
-                    Action action = actionMap.get(key);
-                    if (action != null) {
-                        action.setEnabled(enabled);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * The download dialog that overpass uses.
-     * @since 12576 public
-     */
-    public static final class OverpassDownloadDialog extends DownloadDialog {
-
-        private JosmTextArea overpassQuery;
-        private OverpassQueryList overpassQueryList;
-        private static OverpassDownloadDialog instance;
-        private static final BooleanProperty OVERPASS_QUERY_LIST_OPENED =
-                new BooleanProperty("download.overpass.query-list.opened", false);
-        private static final String ACTION_IMG_SUBDIR = "dialogs";
-
-        private OverpassDownloadDialog(Component parent) {
-            super(parent, ht("/Action/OverpassDownload"));
-            cbDownloadOsmData.setEnabled(false);
-            cbDownloadOsmData.setSelected(false);
-            cbDownloadGpxData.setVisible(false);
-            cbDownloadNotes.setVisible(false);
-            cbStartup.setVisible(false);
-        }
-
-        public static OverpassDownloadDialog getInstance() {
-            if (instance == null) {
-                instance = new OverpassDownloadDialog(Main.parent);
-            }
-            return instance;
-        }
-
-        @Override
-        protected void buildMainPanelAboveDownloadSelections(JPanel pnl) {
-            DisableActionsFocusListener disableActionsFocusListener =
-                    new DisableActionsFocusListener(slippyMapChooser.getNavigationComponentActionMap());
-
-            String tooltip = tr("Build an Overpass query using the Overpass Turbo Query Wizard tool");
-            Action queryWizardAction = new AbstractAction() {
-                @Override
-                public void actionPerformed(ActionEvent e) {
-                    new OverpassQueryWizardDialog(instance).showDialog();
-                }
-            };
-
-            JButton openQueryWizard = new JButton(tr("Query Wizard"));
-            openQueryWizard.setToolTipText(tooltip);
-            openQueryWizard.addActionListener(queryWizardAction);
-
-            // use eol() that is needed for the invisible checkboxes cbDownloadGpxData, cbDownloadNotes
-            pnl.add(openQueryWizard, GBC.eol());
-            pnl.add(new JLabel(tr("Overpass query:")), GBC.std().insets(5, 5, 0, 0).anchor(GBC.NORTHWEST));
-
-            // CHECKSTYLE.OFF: LineLength
-            this.overpassQuery = new JosmTextArea(
-                    "/*\n" +
-                    tr("Place your Overpass query below or generate one using the Overpass Turbo Query Wizard")
-                    + "\n*/",
-                    8, 80);
-            // CHECKSTYLE.ON: LineLength
-            this.overpassQuery.setFont(GuiHelper.getMonospacedFont(overpassQuery));
-            this.overpassQuery.addFocusListener(disableActionsFocusListener);
-            this.overpassQuery.addFocusListener(new FocusListener() {
-                @Override
-                public void focusGained(FocusEvent e) {
-                    overpassQuery.selectAll();
-                }
-
-                @Override
-                public void focusLost(FocusEvent e) {
-                    // ignored
-                }
-            });
-
-
-            this.overpassQueryList = new OverpassQueryList(this, this.overpassQuery);
-            this.overpassQueryList.setPreferredSize(new Dimension(350, 300));
-
-            EditSnippetAction edit = new EditSnippetAction();
-            RemoveSnippetAction remove = new RemoveSnippetAction();
-            this.overpassQueryList.addSelectionListener(edit);
-            this.overpassQueryList.addSelectionListener(remove);
-
-            JPanel listPanel = new JPanel(new GridBagLayout());
-            listPanel.add(new JLabel(tr("Your saved queries:")), GBC.eol().insets(2).anchor(GBC.CENTER));
-            listPanel.add(this.overpassQueryList, GBC.eol().fill(GBC.BOTH));
-            listPanel.add(new JButton(new AddSnippetAction()), GBC.std().fill(GBC.HORIZONTAL));
-            listPanel.add(new JButton(edit), GBC.std().fill(GBC.HORIZONTAL));
-            listPanel.add(new JButton(remove), GBC.std().fill(GBC.HORIZONTAL));
-            listPanel.setVisible(OVERPASS_QUERY_LIST_OPENED.get());
-
-            JScrollPane scrollPane = new JScrollPane(overpassQuery);
-            BasicArrowButton arrowButton = new BasicArrowButton(listPanel.isVisible()
-                ? BasicArrowButton.EAST
-                : BasicArrowButton.WEST);
-            arrowButton.setToolTipText(tr("Show/hide Overpass snippet list"));
-            arrowButton.addActionListener(e -> {
-                if (listPanel.isVisible()) {
-                    listPanel.setVisible(false);
-                    arrowButton.setDirection(BasicArrowButton.WEST);
-                    OVERPASS_QUERY_LIST_OPENED.put(Boolean.FALSE);
-                } else {
-                    listPanel.setVisible(true);
-                    arrowButton.setDirection(BasicArrowButton.EAST);
-                    OVERPASS_QUERY_LIST_OPENED.put(Boolean.TRUE);
-                }
-            });
-
-            JPanel innerPanel = new JPanel(new BorderLayout());
-            innerPanel.add(scrollPane, BorderLayout.CENTER);
-            innerPanel.add(arrowButton, BorderLayout.EAST);
-
-            JPanel pane = new JPanel(new BorderLayout());
-            pane.add(innerPanel, BorderLayout.CENTER);
-            pane.add(listPanel, BorderLayout.EAST);
-
-            GBC gbc = GBC.eol().fill(GBC.HORIZONTAL); gbc.ipady = 200;
-            pnl.add(pane, gbc);
-        }
-
-        public String getOverpassQuery() {
-            return overpassQuery.getText();
-        }
-
-        String getRepairedOverpassQuery() {
-            String query = getOverpassQuery();
-            if (query.matches("(/\\*(\\*[^/]|[^\\*/])*\\*/|\\s)*")) {
-                // Empty query. User might want to download everything
-                boolean doFix = ConditionalOptionPaneUtil.showConfirmationDialog(
-                        "download.overpass.fix.emptytoall",
-                        this,
-                        tr("You entered an empty query. Do you want to download all data in this area instead?"),
-                        tr("Download all data?"),
-                        JOptionPane.YES_NO_OPTION,
-                        JOptionPane.QUESTION_MESSAGE,
-                        JOptionPane.YES_OPTION);
-                if (doFix) {
-                    return "[out:xml]; \n"
-                            + query + "\n"
-                            + "(\n"
-                            + "    node({{bbox}});\n"
-                            + "<;\n"
-                            + ");\n"
-                            + "(._;>;);"
-                            + "out meta;";
-                }
-            }
-            // Note: We can add more repairs here. We might e.g. want to intercept missing 'out meta'.
-            return query;
-        }
-
-        /**
-         * Sets the query that is displayed
-         * @param text The multiline query text.
-         * @since 12576 public
-         */
-        public void setOverpassQuery(String text) {
-            overpassQuery.setText(text);
-        }
-
-        /**
-         * Adds the current query to {@link OverpassQueryList}.
-         * @param overpassQueryToSave The query to save
-         */
-        void saveHistoricItemOnSuccess(String overpassQueryToSave) {
-            overpassQueryList.saveHistoricItem(overpassQueryToSave);
-        }
-
-        @Override
-        protected void updateSizeCheck() {
-            displaySizeCheckResult(false);
-        }
-
-        /**
-         * Triggers the download action to fire.
-         * @since 12576 public
-         */
-        public void triggerDownload() {
-            super.btnDownload.doClick();
-        }
-
-        /**
-         * Action that delegates snippet creation to {@link OverpassQueryList#createNewItem()}.
-         */
-        class AddSnippetAction extends AbstractAction {
-
-            /**
-             * Constructs a new {@code AddSnippetAction}.
-             */
-            AddSnippetAction() {
-                super();
-                putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "add"));
-                putValue(SHORT_DESCRIPTION, tr("Add new snippet"));
-            }
-
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                overpassQueryList.createNewItem();
-            }
-        }
-
-        /**
-         * Action that delegates snippet removal to {@link OverpassQueryList#removeSelectedItem()}.
-         */
-        class RemoveSnippetAction extends AbstractAction implements ListSelectionListener {
-
-            /**
-             * Constructs a new {@code RemoveSnippetAction}.
-             */
-            RemoveSnippetAction() {
-                super();
-                putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "delete"));
-                putValue(SHORT_DESCRIPTION, tr("Delete selected snippet"));
-                checkEnabled();
-            }
-
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                overpassQueryList.removeSelectedItem();
-            }
-
-            /**
-             * Disables the action if no items are selected.
-             */
-            void checkEnabled() {
-                setEnabled(overpassQueryList.getSelectedItem().isPresent());
-            }
-
-            @Override
-            public void valueChanged(ListSelectionEvent e) {
-                checkEnabled();
-            }
-        }
-
-        /**
-         * Action that delegates snippet edit to {@link OverpassQueryList#editSelectedItem()}.
-         */
-        class EditSnippetAction extends AbstractAction implements ListSelectionListener {
-
-            /**
-             * Constructs a new {@code EditSnippetAction}.
-             */
-            EditSnippetAction() {
-                super();
-                putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "edit"));
-                putValue(SHORT_DESCRIPTION, tr("Edit selected snippet"));
-                checkEnabled();
-            }
-
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                overpassQueryList.editSelectedItem();
-            }
-
-            /**
-             * Disables the action if no items are selected.
-             */
-            void checkEnabled() {
-                setEnabled(overpassQueryList.getSelectedItem().isPresent());
-            }
-
-            @Override
-            public void valueChanged(ListSelectionEvent e) {
-                checkEnabled();
-            }
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 12651)
+++ trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 12652)
@@ -73,5 +73,4 @@
 import org.openstreetmap.josm.actions.OrthogonalizeAction;
 import org.openstreetmap.josm.actions.OrthogonalizeAction.Undo;
-import org.openstreetmap.josm.actions.OverpassDownloadAction;
 import org.openstreetmap.josm.actions.PasteAction;
 import org.openstreetmap.josm.actions.PasteAtSourcePositionAction;
@@ -160,6 +159,4 @@
     /** File / Download in current view **/
     public final DownloadOsmInViewAction downloadInView = new DownloadOsmInViewAction();
-    /** File / Download from Overpass API... **/
-    public final OverpassDownloadAction overpassDownload = new OverpassDownloadAction();
     /** File / Download object... **/
     public final DownloadPrimitiveAction downloadPrimitive = new DownloadPrimitiveAction();
@@ -658,5 +655,4 @@
         add(fileMenu, download);
         add(fileMenu, downloadInView, true);
-        add(fileMenu, overpassDownload, true);
         add(fileMenu, downloadPrimitive);
         add(fileMenu, searchNotes);
Index: trunk/src/org/openstreetmap/josm/gui/download/AbstractDownloadSourcePanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/AbstractDownloadSourcePanel.java	(revision 12652)
+++ trunk/src/org/openstreetmap/josm/gui/download/AbstractDownloadSourcePanel.java	(revision 12652)
@@ -0,0 +1,85 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.download;
+
+import org.openstreetmap.josm.data.Bounds;
+
+import javax.swing.Icon;
+import javax.swing.JPanel;
+import java.util.Objects;
+
+/**
+ * GUI representation of {@link DownloadSource} that is shown to the user in
+ * {@link DownloadDialog}.
+ * @param <T> The type of the data that a download source uses.
+ */
+public abstract class AbstractDownloadSourcePanel<T> extends JPanel {
+
+    public AbstractDownloadSourcePanel(final DownloadSource<T> downloadSource) {
+        Objects.requireNonNull(downloadSource);
+        this.downloadSource = downloadSource;
+    }
+
+    /**
+     * The download source of this panel.
+     */
+    protected transient DownloadSource<T> downloadSource;
+
+    /**
+     * Gets the data.
+     * @return Returns the data.
+     */
+    public abstract T getData();
+
+    /**
+     * Gets the download source of this panel.
+     * @return Returns the download source of this panel.
+     */
+    public DownloadSource<T> getDownloadSource() {
+        return this.downloadSource;
+    }
+
+    /**
+     * Saves the current user preferences devoted to the data source.
+     */
+    public abstract void rememberSettings();
+
+    /**
+     * Restores the latest user preferences devoted to the data source.
+     */
+    public abstract void restoreSettings();
+
+    /**
+     * Performs the logic needed in case if the user triggered the download
+     * action in {@link DownloadDialog}.
+     * @return Returns {@code true} if the required procedure of handling the
+     * download action succeeded and {@link DownloadDialog} can be closed, e.g. validation,
+     * otherwise {@code false}.
+     */
+    public abstract boolean checkDownload(Bounds bbox, DownloadSettings settings);
+
+    /**
+     * Performs the logic needed in case if the user triggered the cancel
+     * action in {@link DownloadDialog}.
+     */
+    public void checkCancel() {
+        // nothing, let download dialog to close
+        // override if necessary
+    }
+
+    /**
+     * Gets the icon of the download source panel.
+     * @return The icon. Can be {@code null} if there is no icon associated with
+     * this download source.
+     */
+    public Icon getIcon() {
+        return null;
+    }
+
+    /**
+     * Updates GUI components of the panel according to the bbox changes.
+     * @param bbox The new value for the bounding box.
+     */
+    public void boudingBoxChanged(Bounds bbox) {
+        // override this if the panel must react on bbox changes
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java	(revision 12651)
+++ trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java	(revision 12652)
@@ -6,9 +6,7 @@
 
 import java.awt.BorderLayout;
-import java.awt.Color;
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
-import java.awt.Font;
 import java.awt.Graphics;
 import java.awt.GridBagLayout;
@@ -19,8 +17,11 @@
 import java.awt.event.WindowEvent;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
+import java.util.stream.IntStream;
 
 import javax.swing.AbstractAction;
+import javax.swing.Icon;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
@@ -28,9 +29,8 @@
 import javax.swing.JDialog;
 import javax.swing.JLabel;
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.JSplitPane;
 import javax.swing.JTabbedPane;
 import javax.swing.KeyStroke;
-import javax.swing.event.ChangeListener;
 
 import org.openstreetmap.josm.Main;
@@ -52,17 +52,18 @@
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.OsmUrlToBounds;
-import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.WindowGeometry;
 
 /**
- * Dialog displayed to download OSM and/or GPS data from OSM server.
+ * Dialog displayed to the user to download mapping data.
  */
 public class DownloadDialog extends JDialog {
+
+    /**
+     * Preference properties
+     */
     private static final IntegerProperty DOWNLOAD_TAB = new IntegerProperty("download.tab", 0);
-
+    private static final IntegerProperty DOWNLOAD_SOURCE_TAB = new IntegerProperty("download-source.tab", 0);
+    private static final IntegerProperty DIALOG_SPLIT = new IntegerProperty("download.split", 200);
     private static final BooleanProperty DOWNLOAD_AUTORUN = new BooleanProperty("download.autorun", false);
-    private static final BooleanProperty DOWNLOAD_OSM = new BooleanProperty("download.osm", true);
-    private static final BooleanProperty DOWNLOAD_GPS = new BooleanProperty("download.gps", false);
-    private static final BooleanProperty DOWNLOAD_NOTES = new BooleanProperty("download.notes", false);
     private static final BooleanProperty DOWNLOAD_NEWLAYER = new BooleanProperty("download.newlayer", false);
     private static final BooleanProperty DOWNLOAD_ZOOMTODATA = new BooleanProperty("download.zoomtodata", true);
@@ -83,47 +84,45 @@
     }
 
-    protected SlippyMapChooser slippyMapChooser;
+    protected final transient List<DownloadSource> downloadSources = new ArrayList<>();
     protected final transient List<DownloadSelection> downloadSelections = new ArrayList<>();
     protected final JTabbedPane tpDownloadAreaSelectors = new JTabbedPane();
+    protected final JTabbedPane downloadSourcesTab = new JTabbedPane();
+
     protected JCheckBox cbNewLayer;
     protected JCheckBox cbStartup;
     protected JCheckBox cbZoomToDownloadedData;
-    protected final JLabel sizeCheck = new JLabel();
+    protected SlippyMapChooser slippyMapChooser;
+    protected JPanel mainPanel;
+    protected JSplitPane dialogSplit;
+
+    /*
+     * Keep the reference globally to avoid having it garbage collected
+     */
+    protected final transient ExpertToggleAction.ExpertModeChangeListener expertListener =
+            getExpertModeListenerForDownloadSources();
     protected transient Bounds currentBounds;
     protected boolean canceled;
 
-    protected JCheckBox cbDownloadOsmData;
-    protected JCheckBox cbDownloadGpxData;
-    protected JCheckBox cbDownloadNotes;
-    /** the download action and button */
-    private final DownloadAction actDownload = new DownloadAction();
-    protected final JButton btnDownload = new JButton(actDownload);
-
+    protected JButton btnDownload;
+    protected JButton btnCancel;
+    protected JButton btnHelp;
+
+    /**
+     * Builds the main panel of the dialog.
+     * @return The panel of the dialog.
+     */
     protected final JPanel buildMainPanel() {
-        JPanel pnl = new JPanel(new GridBagLayout());
-
-        // size check depends on selected data source
-        final ChangeListener checkboxChangeListener = e -> updateSizeCheck();
-
-        // adding the download tasks
-        pnl.add(new JLabel(tr("Data Sources and Types:")), GBC.std().insets(5, 5, 1, 5));
-        cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
-        cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
-        cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
-        pnl.add(cbDownloadOsmData, GBC.std().insets(1, 5, 1, 5));
-        cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
-        cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
-        cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener);
-        pnl.add(cbDownloadGpxData, GBC.std().insets(5, 5, 1, 5));
-        cbDownloadNotes = new JCheckBox(tr("Notes"));
-        cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area."));
-        cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener);
-        pnl.add(cbDownloadNotes, GBC.eol().insets(50, 5, 1, 5));
+        mainPanel = new JPanel(new GridBagLayout());
+
+        downloadSources.add(new OSMDownloadSource());
+        downloadSources.add(new OverpassDownloadSource());
+
+        // register all default download sources
+        for (int i = 0; i < downloadSources.size(); i++) {
+            downloadSources.get(i).addGui(this);
+        }
 
         // must be created before hook
         slippyMapChooser = new SlippyMapChooser();
-
-        // hook for subclasses
-        buildMainPanelAboveDownloadSelections(pnl);
 
         // predefined download selections
@@ -137,22 +136,19 @@
         PluginHandler.addDownloadSelection(downloadSelections);
 
-        // now everybody may add their tab to the tabbed pane
-        // (not done right away to allow plugins to remove one of
-        // the default selectors!)
-        for (DownloadSelection s : downloadSelections) {
-            s.addGui(this);
-        }
-
-        pnl.add(tpDownloadAreaSelectors, GBC.eol().fill());
-
-        try {
-            tpDownloadAreaSelectors.setSelectedIndex(DOWNLOAD_TAB.get());
-        } catch (IndexOutOfBoundsException ex) {
-            Logging.trace(ex);
-            DOWNLOAD_TAB.put(0);
-        }
-
-        Font labelFont = sizeCheck.getFont();
-        sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize()));
+        // register all default download selections
+        for (int i = 0; i < downloadSelections.size(); i++) {
+            downloadSelections.get(i).addGui(this);
+        }
+
+        // allow to collapse the panes completely
+        downloadSourcesTab.setMinimumSize(new Dimension(0, 0));
+        tpDownloadAreaSelectors.setMinimumSize(new Dimension(0, 0));
+
+        dialogSplit = new JSplitPane(
+                JSplitPane.VERTICAL_SPLIT,
+                downloadSourcesTab,
+                tpDownloadAreaSelectors);
+
+        mainPanel.add(dialogSplit, GBC.eol().fill());
 
         cbNewLayer = new JCheckBox(tr("Download as new layer"));
@@ -169,18 +165,16 @@
         cbZoomToDownloadedData.setToolTipText(tr("Select to zoom to entire newly downloaded data."));
 
-        pnl.add(cbNewLayer, GBC.std().anchor(GBC.WEST).insets(5, 5, 5, 5));
-        pnl.add(cbStartup, GBC.std().anchor(GBC.WEST).insets(15, 5, 5, 5));
-        pnl.add(cbZoomToDownloadedData, GBC.std().anchor(GBC.WEST).insets(15, 5, 5, 5));
+        mainPanel.add(cbNewLayer, GBC.std().anchor(GBC.WEST).insets(5, 5, 5, 5));
+        mainPanel.add(cbStartup, GBC.std().anchor(GBC.WEST).insets(15, 5, 5, 5));
+        mainPanel.add(cbZoomToDownloadedData, GBC.std().anchor(GBC.WEST).insets(15, 5, 5, 5));
 
         ExpertToggleAction.addVisibilitySwitcher(cbZoomToDownloadedData);
-
-        pnl.add(sizeCheck, GBC.eol().anchor(GBC.EAST).insets(5, 5, 5, 2));
 
         if (!ExpertToggleAction.isExpert()) {
             JLabel infoLabel = new JLabel(
                     tr("Use left click&drag to select area, arrows or right mouse button to scroll map, wheel or +/- to zoom."));
-            pnl.add(infoLabel, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 0, 0));
-        }
-        return pnl;
+            mainPanel.add(infoLabel, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 0, 0));
+        }
+        return mainPanel;
     }
 
@@ -192,32 +186,28 @@
     }
 
+    /**
+     * Builds the button pane of the dialog.
+     * @return The button panel of the dialog.
+     */
     protected final JPanel buildButtonPanel() {
+        btnDownload = new JButton(new DownloadAction());
+        btnCancel = new JButton(new CancelAction());
+        btnHelp = new JButton(
+                new ContextSensitiveHelpAction(getRootPane().getClientProperty("help").toString()));
+
         JPanel pnl = new JPanel(new FlowLayout());
 
-        // -- download button
         pnl.add(btnDownload);
+        pnl.add(btnCancel);
+        pnl.add(btnHelp);
+
         InputMapUtils.enableEnter(btnDownload);
-
-        InputMapUtils.addEnterActionWhenAncestor(cbDownloadGpxData, actDownload);
-        InputMapUtils.addEnterActionWhenAncestor(cbDownloadOsmData, actDownload);
-        InputMapUtils.addEnterActionWhenAncestor(cbDownloadNotes, actDownload);
-        InputMapUtils.addEnterActionWhenAncestor(cbNewLayer, actDownload);
-        InputMapUtils.addEnterActionWhenAncestor(cbStartup, actDownload);
-        InputMapUtils.addEnterActionWhenAncestor(cbZoomToDownloadedData, actDownload);
-
-        // -- cancel button
-        JButton btnCancel;
-        CancelAction actCancel = new CancelAction();
-        btnCancel = new JButton(actCancel);
-        pnl.add(btnCancel);
         InputMapUtils.enableEnter(btnCancel);
-
-        // -- cancel on ESC
-        InputMapUtils.addEscapeAction(getRootPane(), actCancel);
-
-        // -- help button
-        JButton btnHelp = new JButton(new ContextSensitiveHelpAction(getRootPane().getClientProperty("help").toString()));
-        pnl.add(btnHelp);
+        InputMapUtils.addEscapeAction(getRootPane(), btnCancel.getAction());
         InputMapUtils.enableEnter(btnHelp);
+
+        InputMapUtils.addEnterActionWhenAncestor(cbNewLayer, btnDownload.getAction());
+        InputMapUtils.addEnterActionWhenAncestor(cbStartup, btnDownload.getAction());
+        InputMapUtils.addEnterActionWhenAncestor(cbZoomToDownloadedData, btnDownload.getAction());
 
         return pnl;
@@ -261,35 +251,12 @@
         });
         addWindowListener(new WindowEventHandler());
+        ExpertToggleAction.addExpertModeChangeListener(expertListener);
         restoreSettings();
     }
 
-    protected void updateSizeCheck() {
-        boolean isAreaTooLarge = false;
-        if (currentBounds == null) {
-            sizeCheck.setText(tr("No area selected yet"));
-            sizeCheck.setForeground(Color.darkGray);
-        } else if (isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) {
-            // see max_note_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
-            isAreaTooLarge = currentBounds.getArea() > Main.pref.getDouble("osm-server.max-request-area-notes", 25);
-        } else {
-            // see max_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
-            isAreaTooLarge = currentBounds.getArea() > Main.pref.getDouble("osm-server.max-request-area", 0.25);
-        }
-        displaySizeCheckResult(isAreaTooLarge);
-    }
-
-    protected void displaySizeCheckResult(boolean isAreaTooLarge) {
-        if (isAreaTooLarge) {
-            sizeCheck.setText(tr("Download area too large; will probably be rejected by server"));
-            sizeCheck.setForeground(Color.red);
-        } else {
-            sizeCheck.setText(tr("Download area ok, size probably acceptable to server"));
-            sizeCheck.setForeground(Color.darkGray);
-        }
-    }
-
     /**
      * Distributes a "bounding box changed" from one DownloadSelection
-     * object to the others, so they may update or clear their input fields.
+     * object to the others, so they may update or clear their input fields. Also informs
+     * download sources about the change, so they can react on it.
      * @param b new current bounds
      *
@@ -303,5 +270,10 @@
             }
         }
-        updateSizeCheck();
+
+        for (Component ds : downloadSourcesTab.getComponents()) {
+            if (ds instanceof AbstractDownloadSourcePanel) {
+                ((AbstractDownloadSourcePanel) ds).boudingBoxChanged(b);
+            }
+        }
     }
 
@@ -312,32 +284,12 @@
     public void startDownload(Bounds b) {
         this.currentBounds = b;
-        actDownload.run();
-    }
-
-    /**
-     * Replies true if the user selected to download OSM data
-     *
-     * @return true if the user selected to download OSM data
-     */
-    public boolean isDownloadOsmData() {
-        return cbDownloadOsmData.isSelected();
-    }
-
-    /**
-     * Replies true if the user selected to download GPX data
-     *
-     * @return true if the user selected to download GPX data
-     */
-    public boolean isDownloadGpxData() {
-        return cbDownloadGpxData.isSelected();
-    }
-
-    /**
-     * Replies true if user selected to download notes
-     *
-     * @return true if user selected to download notes
-     */
-    public boolean isDownloadNotes() {
-        return cbDownloadNotes.isSelected();
+        startDownload();
+    }
+
+    /**
+     * Starts download.
+     */
+    public void startDownload() {
+        btnDownload.doClick();
     }
 
@@ -362,4 +314,12 @@
 
     /**
+     * Determines if the dialog autorun is enabled in preferences.
+     * @return {@code true} if the download dialog must be open at startup, {@code false} otherwise
+     */
+    public static boolean isAutorunEnabled() {
+        return DOWNLOAD_AUTORUN.get();
+    }
+
+    /**
      * Adds a new download area selector to the download dialog
      *
@@ -372,4 +332,16 @@
 
     /**
+     * Adds a new download source to the download dialog
+     *
+     * @param downloadSource The download source to be added.
+     * @param <T> The type of the download data.
+     */
+    public <T> void addDownloadSource(DownloadSource<T> downloadSource) {
+        if ((ExpertToggleAction.isExpert() && downloadSource.onlyExpert()) || !downloadSource.onlyExpert()) {
+            addNewDownloadSourceTab(downloadSource);
+        }
+    }
+
+    /**
      * Refreshes the tile sources
      * @since 6364
@@ -386,7 +358,6 @@
     public void rememberSettings() {
         DOWNLOAD_TAB.put(tpDownloadAreaSelectors.getSelectedIndex());
-        DOWNLOAD_OSM.put(cbDownloadOsmData.isSelected());
-        DOWNLOAD_GPS.put(cbDownloadGpxData.isSelected());
-        DOWNLOAD_NOTES.put(cbDownloadNotes.isSelected());
+        DOWNLOAD_SOURCE_TAB.put(downloadSourcesTab.getSelectedIndex());
+        DIALOG_SPLIT.put(dialogSplit.getDividerLocation());
         DOWNLOAD_NEWLAYER.put(cbNewLayer.isSelected());
         DOWNLOAD_ZOOMTODATA.put(cbZoomToDownloadedData.isSelected());
@@ -400,12 +371,22 @@
      */
     public void restoreSettings() {
-        cbDownloadOsmData.setSelected(DOWNLOAD_OSM.get());
-        cbDownloadGpxData.setSelected(DOWNLOAD_GPS.get());
-        cbDownloadNotes.setSelected(DOWNLOAD_NOTES.get());
         cbNewLayer.setSelected(DOWNLOAD_NEWLAYER.get());
         cbStartup.setSelected(isAutorunEnabled());
         cbZoomToDownloadedData.setSelected(DOWNLOAD_ZOOMTODATA.get());
-        int idx = Utils.clamp(DOWNLOAD_TAB.get(), 0, tpDownloadAreaSelectors.getTabCount() - 1);
-        tpDownloadAreaSelectors.setSelectedIndex(idx);
+        dialogSplit.setDividerLocation(DIALOG_SPLIT.get());
+
+        try {
+            tpDownloadAreaSelectors.setSelectedIndex(DOWNLOAD_TAB.get());
+        } catch (IndexOutOfBoundsException e) {
+            Main.trace(e);
+            tpDownloadAreaSelectors.setSelectedIndex(0);
+        }
+
+        try {
+            downloadSourcesTab.setSelectedIndex(DOWNLOAD_SOURCE_TAB.get());
+        } catch (IndexOutOfBoundsException e) {
+            Main.trace(e);
+            downloadSourcesTab.setSelectedIndex(0);
+        }
 
         if (MainApplication.isDisplayingMapView()) {
@@ -443,12 +424,4 @@
 
     /**
-     * Determines if the dialog autorun is enabled in preferences.
-     * @return {@code true} if the download dialog must be open at startup, {@code false} otherwise
-     */
-    public static boolean isAutorunEnabled() {
-        return DOWNLOAD_AUTORUN.get();
-    }
-
-    /**
      * Automatically opens the download dialog, if autorun is enabled.
      * @see #isAutorunEnabled
@@ -494,12 +467,75 @@
     }
 
+    /**
+     * Gets the global settings of the download dialog.
+     * @return The {@link DownloadSettings} object that describes the current state of
+     * the download dialog.
+     */
+    public DownloadSettings getDownloadSettings() {
+        return new DownloadSettings(isNewLayerRequired(), isZoomToDownloadedDataRequired());
+    }
+
     protected void setCanceled(boolean canceled) {
         this.canceled = canceled;
     }
 
-    protected void buildMainPanelAboveDownloadSelections(JPanel pnl) {
-        // Do nothing
-    }
-
+    /**
+     * Returns position of the download source in the tabbed pane.
+     * @param downloadSource The download source.
+     * @return The index of the download source, or -1 if it not in the pane.
+     */
+    protected int getDownloadSourceIndex(DownloadSource downloadSource) {
+        return Arrays.stream(downloadSourcesTab.getComponents())
+                .filter(it -> it instanceof AbstractDownloadSourcePanel)
+                .map(it -> (AbstractDownloadSourcePanel) it)
+                .filter(it -> it.getDownloadSource().equals(downloadSource))
+                .findAny()
+                .map(downloadSourcesTab::indexOfComponent)
+                .orElse(-1);
+    }
+
+    /**
+     * Adds the download source to the download sources tab.
+     * @param downloadSource The download source to be added.
+     * @param <T> The type of the download data.
+     */
+    private <T> void addNewDownloadSourceTab(DownloadSource<T> downloadSource) {
+        AbstractDownloadSourcePanel<T> panel = downloadSource.createPanel();
+        downloadSourcesTab.add(panel, downloadSource.getLabel());
+        Icon icon = panel.getIcon();
+        if (icon != null) {
+            int idx = getDownloadSourceIndex(downloadSource);
+            downloadSourcesTab.setIconAt(
+                    idx != -1 ? idx : downloadSourcesTab.getTabCount() - 1,
+                    icon);
+        }
+    }
+
+    /**
+     * Creates listener that removes/adds download sources from/to {@code downloadSourcesTab}
+     * depending on the current mode.
+     * @return The expert mode listener.
+     */
+    private ExpertToggleAction.ExpertModeChangeListener getExpertModeListenerForDownloadSources() {
+        return isExpert -> {
+            if (isExpert) {
+                downloadSources.stream()
+                        .filter(DownloadSource::onlyExpert)
+                        .filter(it -> getDownloadSourceIndex(it) == -1)
+                        .forEach(this::addNewDownloadSourceTab);
+            } else {
+                IntStream.range(0, downloadSourcesTab.getTabCount())
+                        .mapToObj(downloadSourcesTab::getComponentAt)
+                        .filter(it -> it instanceof AbstractDownloadSourcePanel)
+                        .map(it -> (AbstractDownloadSourcePanel) it)
+                        .filter(it -> it.getDownloadSource().onlyExpert())
+                        .forEach(downloadSourcesTab::remove);
+            }
+        };
+    }
+
+    /**
+     * Action that is executed when the cancel button is pressed.
+     */
     class CancelAction extends AbstractAction {
         CancelAction() {
@@ -516,8 +552,13 @@
         @Override
         public void actionPerformed(ActionEvent e) {
+            AbstractDownloadSourcePanel pnl = (AbstractDownloadSourcePanel) downloadSourcesTab.getSelectedComponent();
             run();
-        }
-    }
-
+            pnl.checkCancel();
+        }
+    }
+
+    /**
+     * Action that is executed when the download button is pressed.
+     */
     class DownloadAction extends AbstractAction {
         DownloadAction() {
@@ -529,26 +570,15 @@
 
         public void run() {
-            /*
-             * Checks if the user selected the type of data to download. At least one the following
-             * must be chosen : raw osm data, gpx data, notes.
-             * If none of those are selected, then the corresponding dialog is shown to inform the user.
-             */
-            if (!isDownloadOsmData() && !isDownloadGpxData() && !isDownloadNotes()) {
-                JOptionPane.showMessageDialog(
-                        DownloadDialog.this,
-                        tr("<html>Neither <strong>{0}</strong> nor <strong>{1}</strong> nor <strong>{2}</strong> is enabled.<br>"
-                                        + "Please choose to either download OSM data, or GPX data, or Notes, or all.</html>",
-                                cbDownloadOsmData.getText(),
-                                cbDownloadGpxData.getText(),
-                                cbDownloadNotes.getText()
-                        ),
-                        tr("Error"),
-                        JOptionPane.ERROR_MESSAGE
-                );
-                return;
+            Component panel = downloadSourcesTab.getSelectedComponent();
+            if (panel instanceof AbstractDownloadSourcePanel) {
+                AbstractDownloadSourcePanel pnl = (AbstractDownloadSourcePanel) panel;
+                DownloadSettings downloadSettings = getDownloadSettings();
+                if (pnl.checkDownload(currentBounds, downloadSettings)) {
+                    rememberSettings();
+                    setCanceled(false);
+                    setVisible(false);
+                    pnl.getDownloadSource().doDownload(currentBounds, pnl.getData(), downloadSettings);
+                }
             }
-
-            setCanceled(false);
-            setVisible(false);
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/download/DownloadSettings.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/DownloadSettings.java	(revision 12652)
+++ trunk/src/org/openstreetmap/josm/gui/download/DownloadSettings.java	(revision 12652)
@@ -0,0 +1,38 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.download;
+
+/**
+ * The global settings of {@link DownloadDialog}.
+ */
+public final class DownloadSettings {
+
+    private boolean downloadAsNewLayer;
+    private boolean zoomToDownloadedData;
+
+    /**
+     * Initializes a new instance of {@code DownloadSettings}.
+     * @param downloadAsNewLayer The flag defining if a new layer must be created for the downloaded data.
+     * @param zoomToDownloadedData The flag defining if the map view, see {@link SlippyMapChooser},
+     *                             must zoom to the downloaded data.
+     */
+    public DownloadSettings(boolean downloadAsNewLayer, boolean zoomToDownloadedData) {
+        this.downloadAsNewLayer = downloadAsNewLayer;
+        this.zoomToDownloadedData = zoomToDownloadedData;
+    }
+
+    /**
+     * Gets the flag defining if a new layer must be created for the downloaded data.
+     * @return {@code true} if a new layer must be created, {@code false} otherwise.
+     */
+    public boolean asNewLayer() {
+        return this.downloadAsNewLayer;
+    }
+
+    /**
+     * Gets the flag defining if the map view must zoom to the downloaded data.
+     * @return {@code true} if the view must zoom, {@code false} otherwise.
+     */
+    public boolean zoomToData() {
+        return this.zoomToDownloadedData;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/download/DownloadSource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/DownloadSource.java	(revision 12652)
+++ trunk/src/org/openstreetmap/josm/gui/download/DownloadSource.java	(revision 12652)
@@ -0,0 +1,45 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.download;
+
+
+import org.openstreetmap.josm.data.Bounds;
+
+/**
+ * Defines an interface for different download sources.
+ * @param <T> The type of the data that a download source uses.
+ */
+public interface DownloadSource<T> {
+
+    /**
+     * Creates a panel with GUI specific for the download source.
+     * @return Returns {@link AbstractDownloadSourcePanel}.
+     */
+    AbstractDownloadSourcePanel<T> createPanel();
+
+    /**
+     * Downloads the data.
+     * @param bbox The bounding box. Can be null if no bounding box selected.
+     * @param data The required data for the download source.
+     * @param settings The global settings of the download dialog, see {@link DownloadDialog}.
+     */
+    void doDownload(Bounds bbox, T data, DownloadSettings settings);
+
+    /**
+     * Returns a string representation of this download source.
+     * @return A string representation of this download source.
+     */
+    String getLabel();
+
+    /**
+     * Add a download source to the dialog, see {@link DownloadDialog}.
+     * @param dialog The download dialog.
+     */
+    void addGui(DownloadDialog dialog);
+
+    /**
+     * Defines whether this download source should be visible only in the expert mode.
+     * @return Returns {@code true} if the download source should be visible only in the
+     * expert mode, {@code false} otherwise.
+     */
+    boolean onlyExpert();
+}
Index: trunk/src/org/openstreetmap/josm/gui/download/OSMDownloadSource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/OSMDownloadSource.java	(revision 12652)
+++ trunk/src/org/openstreetmap/josm/gui/download/OSMDownloadSource.java	(revision 12652)
@@ -0,0 +1,328 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.download;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.GridBagLayout;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import javax.swing.Icon;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.event.ChangeListener;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.downloadtasks.AbstractDownloadTask;
+import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask;
+import org.openstreetmap.josm.actions.downloadtasks.DownloadNotesTask;
+import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
+import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.ProjectionBounds;
+import org.openstreetmap.josm.data.ViewportData;
+import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Pair;
+
+/**
+ * Class defines the way data is fetched from the OSM server.
+ */
+public class OSMDownloadSource implements DownloadSource<OSMDownloadSource.OSMDownloadData> {
+
+    @Override
+    public AbstractDownloadSourcePanel<OSMDownloadData> createPanel() {
+        return new OSMDownloadSourcePanel(this);
+    }
+
+    @Override
+    public void doDownload(Bounds bbox, OSMDownloadData data, DownloadSettings settings) {
+        boolean zoom = settings.zoomToData();
+        boolean newLayer = settings.asNewLayer();
+        List<Pair<AbstractDownloadTask<?>, Future<?>>> tasks = new ArrayList<>();
+
+        if (data.isDownloadOSMData()) {
+            DownloadOsmTask task = new DownloadOsmTask();
+            task.setZoomAfterDownload(zoom && !data.isDownloadGPX() && !data.isDownloadNotes());
+            Future<?> future = task.download(newLayer, bbox, null);
+            MainApplication.worker.submit(new PostDownloadHandler(task, future));
+            if (zoom) {
+                tasks.add(new Pair<>(task, future));
+            }
+        }
+
+        if (data.isDownloadGPX()) {
+            DownloadGpsTask task = new DownloadGpsTask();
+            task.setZoomAfterDownload(zoom && !data.isDownloadOSMData() && !data.isDownloadNotes());
+            Future<?> future = task.download(newLayer, bbox, null);
+            MainApplication.worker.submit(new PostDownloadHandler(task, future));
+            if (zoom) {
+                tasks.add(new Pair<>(task, future));
+            }
+        }
+
+        if (data.isDownloadNotes()) {
+            DownloadNotesTask task = new DownloadNotesTask();
+            task.setZoomAfterDownload(zoom && !data.isDownloadOSMData() && !data.isDownloadGPX());
+            Future<?> future = task.download(false, bbox, null);
+            MainApplication.worker.submit(new PostDownloadHandler(task, future));
+            if (zoom) {
+                tasks.add(new Pair<>(task, future));
+            }
+        }
+
+        if (zoom && tasks.size() > 1) {
+            MainApplication.worker.submit(() -> {
+                ProjectionBounds bounds = null;
+                // Wait for completion of download jobs
+                for (Pair<AbstractDownloadTask<?>, Future<?>> p : tasks) {
+                    try {
+                        p.b.get();
+                        ProjectionBounds b = p.a.getDownloadProjectionBounds();
+                        if (bounds == null) {
+                            bounds = b;
+                        } else if (b != null) {
+                            bounds.extend(b);
+                        }
+                    } catch (InterruptedException | ExecutionException ex) {
+                        Logging.warn(ex);
+                    }
+                }
+                MapFrame map = MainApplication.getMap();
+                // Zoom to the larger download bounds
+                if (map != null && bounds != null) {
+                    final ProjectionBounds pb = bounds;
+                    GuiHelper.runInEDTAndWait(() -> map.mapView.zoomTo(new ViewportData(pb)));
+                }
+            });
+        }
+    }
+
+    @Override
+    public String getLabel() {
+        return tr("Download from OSM");
+    }
+
+    @Override
+    public void addGui(DownloadDialog dialog) {
+        dialog.addDownloadSource(this);
+    }
+
+    @Override
+    public boolean onlyExpert() {
+        return false;
+    }
+
+    /**
+     * The GUI representation of the OSM download source.
+     */
+    public static class OSMDownloadSourcePanel extends AbstractDownloadSourcePanel<OSMDownloadData> {
+
+        private final JCheckBox cbDownloadOsmData;
+        private final JCheckBox cbDownloadGpxData;
+        private final JCheckBox cbDownloadNotes;
+        private final JLabel sizeCheck = new JLabel();
+
+        private static final BooleanProperty DOWNLOAD_OSM = new BooleanProperty("download.osm.data", true);
+        private static final BooleanProperty DOWNLOAD_GPS = new BooleanProperty("download.osm.gps", false);
+        private static final BooleanProperty DOWNLOAD_NOTES = new BooleanProperty("download.osm.notes", false);
+
+        public OSMDownloadSourcePanel(OSMDownloadSource ds) {
+            super(ds);
+            setLayout(new GridBagLayout());
+
+            // size check depends on selected data source
+            final ChangeListener checkboxChangeListener = e ->
+                    DownloadDialog.getInstance().getSelectedDownloadArea().ifPresent(this::updateSizeCheck);
+
+            // adding the download tasks
+            add(new JLabel(tr("Data Sources and Types:")), GBC.std().insets(5, 5, 1, 5).anchor(GBC.CENTER));
+            cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
+            cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
+            cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
+
+            cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
+            cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
+            cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener);
+
+            cbDownloadNotes = new JCheckBox(tr("Notes"));
+            cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area."));
+            cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener);
+
+            Font labelFont = sizeCheck.getFont();
+            sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize()));
+
+            add(cbDownloadOsmData, GBC.std().insets(1, 5, 1, 5));
+            add(cbDownloadGpxData, GBC.std().insets(1, 5, 1, 5));
+            add(cbDownloadNotes, GBC.eol().insets(1, 5, 1, 5));
+            add(sizeCheck, GBC.eol().anchor(GBC.EAST).insets(5, 5, 5, 2));
+        }
+
+        @Override
+        public OSMDownloadData getData() {
+            return new OSMDownloadData(
+                    isDownloadOsmData(),
+                    isDownloadNotes(),
+                    isDownloadGpxData());
+        }
+
+        @Override
+        public void rememberSettings() {
+            DOWNLOAD_OSM.put(isDownloadOsmData());
+            DOWNLOAD_GPS.put(isDownloadGpxData());
+            DOWNLOAD_NOTES.put(isDownloadNotes());
+        }
+
+        @Override
+        public void restoreSettings() {
+            cbDownloadOsmData.setSelected(DOWNLOAD_OSM.get());
+            cbDownloadGpxData.setSelected(DOWNLOAD_GPS.get());
+            cbDownloadNotes.setSelected(DOWNLOAD_NOTES.get());
+        }
+
+        @Override
+        public boolean checkDownload(Bounds bbox, DownloadSettings settings) {
+            /*
+             * It is mandatory to specify the area to download from OSM.
+             */
+            if (bbox == null) {
+                JOptionPane.showMessageDialog(
+                        this.getParent(),
+                        tr("Please select a download area first."),
+                        tr("Error"),
+                        JOptionPane.ERROR_MESSAGE
+                );
+
+                return false;
+            }
+
+            /*
+             * Checks if the user selected the type of data to download. At least one the following
+             * must be chosen : raw osm data, gpx data, notes.
+             * If none of those are selected, then the corresponding dialog is shown to inform the user.
+             */
+            if (!isDownloadOsmData() && !isDownloadGpxData() && !isDownloadNotes()) {
+                JOptionPane.showMessageDialog(
+                        this.getParent(),
+                        tr("<html>Neither <strong>{0}</strong> nor <strong>{1}</strong> nor <strong>{2}</strong> is enabled.<br>"
+                                        + "Please choose to either download OSM data, or GPX data, or Notes, or all.</html>",
+                                cbDownloadOsmData.getText(),
+                                cbDownloadGpxData.getText(),
+                                cbDownloadNotes.getText()
+                        ),
+                        tr("Error"),
+                        JOptionPane.ERROR_MESSAGE
+                );
+
+                return false;
+            }
+
+            this.rememberSettings();
+
+            return true;
+        }
+
+        /**
+         * Replies true if the user selected to download OSM data
+         *
+         * @return true if the user selected to download OSM data
+         */
+        public boolean isDownloadOsmData() {
+            return cbDownloadOsmData.isSelected();
+        }
+
+        /**
+         * Replies true if the user selected to download GPX data
+         *
+         * @return true if the user selected to download GPX data
+         */
+        public boolean isDownloadGpxData() {
+            return cbDownloadGpxData.isSelected();
+        }
+
+        /**
+         * Replies true if user selected to download notes
+         *
+         * @return true if user selected to download notes
+         */
+        public boolean isDownloadNotes() {
+            return cbDownloadNotes.isSelected();
+        }
+
+        @Override
+        public Icon getIcon() {
+            return ImageProvider.get("download");
+        }
+
+        @Override
+        public void boudingBoxChanged(Bounds bbox) {
+            updateSizeCheck(bbox);
+        }
+
+        private void updateSizeCheck(Bounds bbox) {
+            boolean isAreaTooLarge = false;
+            if (bbox == null) {
+                sizeCheck.setText(tr("No area selected yet"));
+                sizeCheck.setForeground(Color.darkGray);
+            } else if (!isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) {
+                isAreaTooLarge = false;
+            } else if (isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) {
+                // see max_note_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
+                isAreaTooLarge = bbox.getArea() > Main.pref.getDouble("osm-server.max-request-area-notes", 25);
+            } else {
+                // see max_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
+                isAreaTooLarge = bbox.getArea() > Main.pref.getDouble("osm-server.max-request-area", 0.25);
+            }
+
+            displaySizeCheckResult(isAreaTooLarge);
+        }
+
+        private void displaySizeCheckResult(boolean isAreaTooLarge) {
+            if (isAreaTooLarge) {
+                sizeCheck.setText(tr("Download area too large; will probably be rejected by server"));
+                sizeCheck.setForeground(Color.red);
+            } else {
+                sizeCheck.setText(tr("Download area ok, size probably acceptable to server"));
+                sizeCheck.setForeground(Color.darkGray);
+            }
+        }
+
+    }
+
+    /**
+     * Encapsulates data that is required to download from the OSM server.
+     */
+    static class OSMDownloadData {
+        private boolean downloadOSMData;
+        private boolean downloadNotes;
+        private boolean downloadGPX;
+
+        OSMDownloadData(boolean downloadOSMData, boolean downloadNotes, boolean downloadGPX) {
+            this.downloadOSMData = downloadOSMData;
+            this.downloadNotes = downloadNotes;
+            this.downloadGPX = downloadGPX;
+        }
+
+        boolean isDownloadOSMData() {
+            return downloadOSMData;
+        }
+
+        boolean isDownloadNotes() {
+            return downloadNotes;
+        }
+
+        boolean isDownloadGPX() {
+            return downloadGPX;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/download/OverpassDownloadSource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/OverpassDownloadSource.java	(revision 12652)
+++ trunk/src/org/openstreetmap/josm/gui/download/OverpassDownloadSource.java	(revision 12652)
@@ -0,0 +1,378 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.download;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.util.Collection;
+import java.util.concurrent.Future;
+import java.util.function.Consumer;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.plaf.basic.BasicArrowButton;
+
+import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
+import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.preferences.server.OverpassServerPreference;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.gui.widgets.JosmTextArea;
+import org.openstreetmap.josm.io.OverpassDownloadReader;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Class defines the way data is fetched from Overpass API.
+ */
+public class OverpassDownloadSource implements DownloadSource<OverpassDownloadSource.OverpassDownloadData> {
+
+    @Override
+    public AbstractDownloadSourcePanel<OverpassDownloadData> createPanel() {
+        return new OverpassDownloadSourcePanel(this);
+    }
+
+    @Override
+    public void doDownload(Bounds bbox, OverpassDownloadData data, DownloadSettings settings) {
+        /*
+         * In order to support queries generated by the Overpass Turbo Query Wizard tool
+         * which do not require the area to be specified.
+         */
+        Bounds area = bbox != null ? bbox : new Bounds(0, 0, 0, 0);
+        DownloadOsmTask task = new DownloadOsmTask();
+        task.setZoomAfterDownload(settings.zoomToData());
+        Future<?> future = task.download(
+                new OverpassDownloadReader(area, OverpassServerPreference.getOverpassServer(), data.getQuery()),
+                settings.asNewLayer(), area, null);
+        MainApplication.worker.submit(new PostDownloadHandler(task, future, data.getErrorReporter()));
+    }
+
+    @Override
+    public String getLabel() {
+        return tr("Download from Overpass API");
+    }
+
+    @Override
+    public void addGui(DownloadDialog dialog) {
+        dialog.addDownloadSource(this);
+    }
+
+    @Override
+    public boolean onlyExpert() {
+        return true;
+    }
+
+    /**
+     * The GUI representation of the Overpass download source.
+     */
+    public static class OverpassDownloadSourcePanel extends AbstractDownloadSourcePanel<OverpassDownloadData> {
+
+        private JosmTextArea overpassQuery;
+        private OverpassQueryList overpassQueryList;
+
+        private static final BooleanProperty OVERPASS_QUERY_LIST_OPENED =
+                new BooleanProperty("download.overpass.query-list.opened", false);
+        private static final String ACTION_IMG_SUBDIR = "dialogs";
+
+        public OverpassDownloadSourcePanel(OverpassDownloadSource ds) {
+            super(ds);
+            setLayout(new BorderLayout());
+
+            String tooltip = tr("Build an Overpass query using the Overpass Turbo Query Wizard tool");
+            Action queryWizardAction = new AbstractAction() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    new OverpassQueryWizardDialog(OverpassDownloadSourcePanel.this).showDialog();
+                }
+            };
+
+            JButton openQueryWizard = new JButton(tr("Query Wizard"));
+            openQueryWizard.setToolTipText(tooltip);
+            openQueryWizard.addActionListener(queryWizardAction);
+
+            // CHECKSTYLE.OFF: LineLength
+            this.overpassQuery = new JosmTextArea(
+                    "/*\n" +
+                            tr("Place your Overpass query below or generate one using the Overpass Turbo Query Wizard")
+                            + "\n*/",
+                    8, 80);
+            // CHECKSTYLE.ON: LineLength
+            this.overpassQuery.setFont(GuiHelper.getMonospacedFont(overpassQuery));
+            this.overpassQuery.addFocusListener(new FocusListener() {
+                @Override
+                public void focusGained(FocusEvent e) {
+                    overpassQuery.selectAll();
+                }
+
+                @Override
+                public void focusLost(FocusEvent e) {
+                    // ignored
+                }
+            });
+
+
+            this.overpassQueryList = new OverpassQueryList(this, this.overpassQuery);
+            this.overpassQueryList.setPreferredSize(new Dimension(350, 300));
+
+            EditSnippetAction edit = new EditSnippetAction();
+            RemoveSnippetAction remove = new RemoveSnippetAction();
+            this.overpassQueryList.addSelectionListener(edit);
+            this.overpassQueryList.addSelectionListener(remove);
+
+            JPanel listPanel = new JPanel(new GridBagLayout());
+            listPanel.add(new JLabel(tr("Your saved queries:")), GBC.eol().insets(2).anchor(GBC.CENTER));
+            listPanel.add(this.overpassQueryList, GBC.eol().fill(GBC.BOTH));
+            listPanel.add(new JButton(new AddSnippetAction()), GBC.std().fill(GBC.HORIZONTAL));
+            listPanel.add(new JButton(edit), GBC.std().fill(GBC.HORIZONTAL));
+            listPanel.add(new JButton(remove), GBC.std().fill(GBC.HORIZONTAL));
+            listPanel.setVisible(OVERPASS_QUERY_LIST_OPENED.get());
+
+            JScrollPane scrollPane = new JScrollPane(overpassQuery);
+            BasicArrowButton arrowButton = new BasicArrowButton(listPanel.isVisible()
+                    ? BasicArrowButton.EAST
+                    : BasicArrowButton.WEST);
+            arrowButton.setToolTipText(tr("Show/hide Overpass snippet list"));
+            arrowButton.addActionListener(e -> {
+                if (listPanel.isVisible()) {
+                    listPanel.setVisible(false);
+                    arrowButton.setDirection(BasicArrowButton.WEST);
+                    OVERPASS_QUERY_LIST_OPENED.put(Boolean.FALSE);
+                } else {
+                    listPanel.setVisible(true);
+                    arrowButton.setDirection(BasicArrowButton.EAST);
+                    OVERPASS_QUERY_LIST_OPENED.put(Boolean.TRUE);
+                }
+            });
+
+            JPanel innerPanel = new JPanel(new BorderLayout());
+            innerPanel.add(scrollPane, BorderLayout.CENTER);
+            innerPanel.add(arrowButton, BorderLayout.EAST);
+
+            JPanel leftPanel = new JPanel(new GridBagLayout());
+            leftPanel.add(new JLabel(tr("Overpass query:")), GBC.eol().insets(5, 1, 5, 1).anchor(GBC.NORTHWEST));
+            leftPanel.add(new JLabel(), GBC.eol().fill(GBC.VERTICAL));
+            leftPanel.add(openQueryWizard, GBC.eol().anchor(GBC.CENTER));
+            leftPanel.add(new JLabel(), GBC.eol().fill(GBC.VERTICAL));
+
+            add(leftPanel, BorderLayout.WEST);
+            add(innerPanel, BorderLayout.CENTER);
+            add(listPanel, BorderLayout.EAST);
+        }
+
+        @Override
+        public OverpassDownloadData getData() {
+            String query = overpassQuery.getText();
+            /*
+             * A callback that is passed to PostDownloadReporter that is called once the download task
+             * has finished. According to the number of errors happened, their type we decide whether we
+             * want to save the last query in OverpassQueryList.
+             */
+            Consumer<Collection<Object>> errorReporter = errors -> {
+
+                boolean onlyNoDataError = errors.size() == 1 &&
+                        errors.contains("No data found in this area.");
+
+                if (errors.isEmpty() || onlyNoDataError) {
+                    overpassQueryList.saveHistoricItem(query);
+                }
+            };
+
+            return new OverpassDownloadData(query, errorReporter);
+        }
+
+        @Override
+        public void rememberSettings() {
+            // nothing
+        }
+
+        @Override
+        public void restoreSettings() {
+            // nothing
+        }
+
+        @Override
+        public boolean checkDownload(Bounds bbox, DownloadSettings settings) {
+            String query = getData().getQuery();
+
+            /*
+             * Absence of the selected area can be justified only if the overpass query
+             * is not restricted to bbox.
+             */
+            if (bbox == null && query.contains("{{bbox}}")) {
+                JOptionPane.showMessageDialog(
+                        this.getParent(),
+                        tr("Please select a download area first."),
+                        tr("Error"),
+                        JOptionPane.ERROR_MESSAGE
+                );
+                return false;
+            }
+
+            /*
+             * Check for an empty query. User might want to download everything, if so validation is passed,
+             * otherwise return false.
+             */
+            if (query.matches("(/\\*(\\*[^/]|[^\\*/])*\\*/|\\s)*")) {
+                boolean doFix = ConditionalOptionPaneUtil.showConfirmationDialog(
+                        "download.overpass.fix.emptytoall",
+                        this,
+                        tr("You entered an empty query. Do you want to download all data in this area instead?"),
+                        tr("Download all data?"),
+                        JOptionPane.YES_NO_OPTION,
+                        JOptionPane.QUESTION_MESSAGE,
+                        JOptionPane.YES_OPTION);
+                if (doFix) {
+                    String repairedQuery = "[out:xml]; \n"
+                            + query + "\n"
+                            + "(\n"
+                            + "    node({{bbox}});\n"
+                            + "<;\n"
+                            + ");\n"
+                            + "(._;>;);"
+                            + "out meta;";
+                    this.overpassQuery.setText(repairedQuery);
+                } else {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        /**
+         * Sets query to the query text field.
+         * @param query The query to set.
+         */
+        public void setOverpassQuery(String query) {
+            this.overpassQuery.setText(query);
+        }
+
+        @Override
+        public Icon getIcon() {
+            return ImageProvider.get("download-overpass");
+        }
+
+        /**
+         * Action that delegates snippet creation to {@link OverpassQueryList#createNewItem()}.
+         */
+        private class AddSnippetAction extends AbstractAction {
+
+            /**
+             * Constructs a new {@code AddSnippetAction}.
+             */
+            AddSnippetAction() {
+                super();
+                putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "add"));
+                putValue(SHORT_DESCRIPTION, tr("Add new snippet"));
+            }
+
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                overpassQueryList.createNewItem();
+            }
+        }
+
+        /**
+         * Action that delegates snippet removal to {@link OverpassQueryList#removeSelectedItem()}.
+         */
+        private class RemoveSnippetAction extends AbstractAction implements ListSelectionListener {
+
+            /**
+             * Constructs a new {@code RemoveSnippetAction}.
+             */
+            RemoveSnippetAction() {
+                super();
+                putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "delete"));
+                putValue(SHORT_DESCRIPTION, tr("Delete selected snippet"));
+                checkEnabled();
+            }
+
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                overpassQueryList.removeSelectedItem();
+            }
+
+            /**
+             * Disables the action if no items are selected.
+             */
+            void checkEnabled() {
+                setEnabled(overpassQueryList.getSelectedItem().isPresent());
+            }
+
+            @Override
+            public void valueChanged(ListSelectionEvent e) {
+                checkEnabled();
+            }
+        }
+
+        /**
+         * Action that delegates snippet edit to {@link OverpassQueryList#editSelectedItem()}.
+         */
+        private class EditSnippetAction extends AbstractAction implements ListSelectionListener {
+
+            /**
+             * Constructs a new {@code EditSnippetAction}.
+             */
+            EditSnippetAction() {
+                super();
+                putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "edit"));
+                putValue(SHORT_DESCRIPTION, tr("Edit selected snippet"));
+                checkEnabled();
+            }
+
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                overpassQueryList.editSelectedItem();
+            }
+
+            /**
+             * Disables the action if no items are selected.
+             */
+            void checkEnabled() {
+                setEnabled(overpassQueryList.getSelectedItem().isPresent());
+            }
+
+            @Override
+            public void valueChanged(ListSelectionEvent e) {
+                checkEnabled();
+            }
+        }
+    }
+
+    /**
+     * Encapsulates data that is required to preform download from Overpass API.
+     */
+    static class OverpassDownloadData {
+        private String query;
+        private Consumer<Collection<Object>> errorReporter;
+
+        OverpassDownloadData(String query, Consumer<Collection<Object>> errorReporter) {
+            this.query = query;
+            this.errorReporter = errorReporter;
+        }
+
+        String getQuery() {
+            return this.query;
+        }
+
+        Consumer<Collection<Object>> getErrorReporter() {
+            return this.errorReporter;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/download/OverpassQueryWizardDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/OverpassQueryWizardDialog.java	(revision 12651)
+++ trunk/src/org/openstreetmap/josm/gui/download/OverpassQueryWizardDialog.java	(revision 12652)
@@ -20,5 +20,4 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.OverpassDownloadAction.OverpassDownloadDialog;
 import org.openstreetmap.josm.data.preferences.CollectionProperty;
 import org.openstreetmap.josm.gui.ExtendedDialog;
@@ -64,14 +63,14 @@
             + "</style>\n";
 
-    private final OverpassDownloadDialog parentDialog;
+    private final OverpassDownloadSource.OverpassDownloadSourcePanel dsPanel;
 
     /**
      * Create a new {@link OverpassQueryWizardDialog}
-     * @param parentDialog The parent this dialog should be displayed for
-     */
-    public OverpassQueryWizardDialog(OverpassDownloadDialog parentDialog) {
-        super(parentDialog, tr("Overpass Turbo Query Wizard"),
+     * @param dsPanel The Overpass download source panel.
+     */
+    public OverpassQueryWizardDialog(OverpassDownloadSource.OverpassDownloadSourcePanel dsPanel) {
+        super(dsPanel.getParent(), tr("Overpass Turbo Query Wizard"),
                 tr("Build query"), tr("Build query and execute"), tr("Cancel"));
-        this.parentDialog = parentDialog;
+        this.dsPanel = dsPanel;
 
         this.queryWizard = new HistoryComboBox();
@@ -91,6 +90,6 @@
         queryWizard.setPossibleItems(OVERPASS_WIZARD_HISTORY.get());
 
-        setCancelButton(CANCEL);
-        setDefaultButton(BUILD_AN_EXECUTE_QUERY + 1); // Build and execute button
+        setCancelButton(CANCEL + 1);
+        setDefaultButton(BUILD_AN_EXECUTE_QUERY + 1);
         setContent(panel, false);
     }
@@ -110,5 +109,5 @@
                     super.buttonAction(BUILD_AN_EXECUTE_QUERY, evt);
 
-                    parentDialog.triggerDownload();
+                    DownloadDialog.getInstance().startDownload();
                 }
                 break;
@@ -143,5 +142,5 @@
             Logging.error(ex);
             JOptionPane.showMessageDialog(
-                    parentDialog,
+                    dsPanel.getParent(),
                     "<html>" +
                      tr("The Overpass wizard could not parse the following query:") +
@@ -166,5 +165,5 @@
         if (q.isPresent()) {
             String query = q.get();
-            parentDialog.setOverpassQuery(query);
+            dsPanel.setOverpassQuery(query);
 
             return true;
