Ticket #15167: merge-download-dialog-v2.patch
File merge-download-dialog-v2.patch, 83.9 KB (added by , 8 years ago) |
---|
-
src/org/openstreetmap/josm/actions/DownloadAction.java
6 6 7 7 import java.awt.event.ActionEvent; 8 8 import java.awt.event.KeyEvent; 9 import java.util.ArrayList;10 import java.util.List;11 import java.util.Optional;12 import java.util.concurrent.ExecutionException;13 import java.util.concurrent.Future;14 9 15 import javax.swing.JOptionPane;16 17 import org.openstreetmap.josm.Main;18 import org.openstreetmap.josm.actions.downloadtasks.AbstractDownloadTask;19 import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask;20 import org.openstreetmap.josm.actions.downloadtasks.DownloadNotesTask;21 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;22 import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;23 import org.openstreetmap.josm.data.Bounds;24 import org.openstreetmap.josm.data.ProjectionBounds;25 import org.openstreetmap.josm.data.ViewportData;26 10 import org.openstreetmap.josm.gui.download.DownloadDialog; 27 import org.openstreetmap.josm.gui.util.GuiHelper;28 import org.openstreetmap.josm.tools.Pair;29 11 import org.openstreetmap.josm.tools.Shortcut; 30 12 31 13 /** … … 42 24 * Constructs a new {@code DownloadAction}. 43 25 */ 44 26 public DownloadAction() { 45 super(tr("Download from OSM..."), "download", tr("Download map data from the OSM server."),46 Shortcut.registerShortcut("file:download", tr("File: {0}", tr("Download from OSM...")), KeyEvent.VK_DOWN, Shortcut.CTRL_SHIFT),27 super(tr("Download data"), "download", tr("Download map data from a server of your choice"), 28 Shortcut.registerShortcut("file:download", tr("File: {0}", tr("Download data")), KeyEvent.VK_DOWN, Shortcut.CTRL_SHIFT), 47 29 true); 48 30 putValue("help", ht("/Action/Download")); 49 31 } … … 53 35 DownloadDialog dialog = DownloadDialog.getInstance(); 54 36 dialog.restoreSettings(); 55 37 dialog.setVisible(true); 56 57 if (dialog.isCanceled()) {58 return;59 }60 61 dialog.rememberSettings();62 63 Optional<Bounds> selectedArea = dialog.getSelectedDownloadArea();64 if (!selectedArea.isPresent()) {65 JOptionPane.showMessageDialog(66 dialog,67 tr("Please select a download area first."),68 tr("Error"),69 JOptionPane.ERROR_MESSAGE70 );71 return;72 }73 74 final Bounds area = selectedArea.get();75 final boolean zoom = dialog.isZoomToDownloadedDataRequired();76 final List<Pair<AbstractDownloadTask<?>, Future<?>>> tasks = new ArrayList<>();77 78 if (dialog.isDownloadOsmData()) {79 DownloadOsmTask task = new DownloadOsmTask();80 task.setZoomAfterDownload(zoom && !dialog.isDownloadGpxData() && !dialog.isDownloadNotes());81 Future<?> future = task.download(dialog.isNewLayerRequired(), area, null);82 Main.worker.submit(new PostDownloadHandler(task, future));83 if (zoom) {84 tasks.add(new Pair<>(task, future));85 }86 }87 88 if (dialog.isDownloadGpxData()) {89 DownloadGpsTask task = new DownloadGpsTask();90 task.setZoomAfterDownload(zoom && !dialog.isDownloadOsmData() && !dialog.isDownloadNotes());91 Future<?> future = task.download(dialog.isNewLayerRequired(), area, null);92 Main.worker.submit(new PostDownloadHandler(task, future));93 if (zoom) {94 tasks.add(new Pair<>(task, future));95 }96 }97 98 if (dialog.isDownloadNotes()) {99 DownloadNotesTask task = new DownloadNotesTask();100 task.setZoomAfterDownload(zoom && !dialog.isDownloadOsmData() && !dialog.isDownloadGpxData());101 Future<?> future = task.download(false, area, null);102 Main.worker.submit(new PostDownloadHandler(task, future));103 if (zoom) {104 tasks.add(new Pair<>(task, future));105 }106 }107 108 if (zoom && tasks.size() > 1) {109 Main.worker.submit(() -> {110 ProjectionBounds bounds = null;111 // Wait for completion of download jobs112 for (Pair<AbstractDownloadTask<?>, Future<?>> p : tasks) {113 try {114 p.b.get();115 ProjectionBounds b = p.a.getDownloadProjectionBounds();116 if (bounds == null) {117 bounds = b;118 } else if (b != null) {119 bounds.extend(b);120 }121 } catch (InterruptedException | ExecutionException ex) {122 Main.warn(ex);123 }124 }125 // Zoom to the larger download bounds126 if (Main.map != null && bounds != null) {127 final ProjectionBounds pb = bounds;128 GuiHelper.runInEDTAndWait(() -> Main.map.mapView.zoomTo(new ViewportData(pb)));129 }130 });131 }132 38 } 133 39 } -
src/org/openstreetmap/josm/actions/OverpassDownloadAction.java
1 // License: GPL. For details, see LICENSE file.2 package org.openstreetmap.josm.actions;3 4 import static org.openstreetmap.josm.gui.help.HelpUtil.ht;5 import static org.openstreetmap.josm.tools.I18n.tr;6 7 import java.awt.BorderLayout;8 import java.awt.Component;9 import java.awt.Dimension;10 import java.awt.GridBagLayout;11 import java.awt.event.ActionEvent;12 import java.awt.event.FocusEvent;13 import java.awt.event.FocusListener;14 import java.awt.event.KeyEvent;15 import java.util.Collection;16 import java.util.Optional;17 import java.util.concurrent.Future;18 import java.util.function.Consumer;19 20 import javax.swing.AbstractAction;21 import javax.swing.Action;22 import javax.swing.ActionMap;23 import javax.swing.JButton;24 import javax.swing.JLabel;25 import javax.swing.JOptionPane;26 import javax.swing.JPanel;27 import javax.swing.JScrollPane;28 import javax.swing.event.ListSelectionEvent;29 import javax.swing.event.ListSelectionListener;30 import javax.swing.plaf.basic.BasicArrowButton;31 32 import org.openstreetmap.josm.Main;33 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;34 import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;35 import org.openstreetmap.josm.data.Bounds;36 import org.openstreetmap.josm.data.preferences.BooleanProperty;37 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;38 import org.openstreetmap.josm.gui.download.DownloadDialog;39 import org.openstreetmap.josm.gui.download.OverpassQueryList;40 import org.openstreetmap.josm.gui.download.OverpassQueryWizardDialog;41 import org.openstreetmap.josm.gui.preferences.server.OverpassServerPreference;42 import org.openstreetmap.josm.gui.util.GuiHelper;43 import org.openstreetmap.josm.gui.widgets.JosmTextArea;44 import org.openstreetmap.josm.io.OverpassDownloadReader;45 import org.openstreetmap.josm.tools.GBC;46 import org.openstreetmap.josm.tools.ImageProvider;47 import org.openstreetmap.josm.tools.Shortcut;48 49 /**50 * Download map data from Overpass API server.51 * @since 868452 */53 public class OverpassDownloadAction extends JosmAction {54 55 /**56 * Constructs a new {@code OverpassDownloadAction}.57 */58 public OverpassDownloadAction() {59 super(tr("Download from Overpass API ..."), "download-overpass", tr("Download map data from Overpass API server."),60 // CHECKSTYLE.OFF: LineLength61 Shortcut.registerShortcut("file:download-overpass", tr("File: {0}", tr("Download from Overpass API ...")), KeyEvent.VK_DOWN, Shortcut.ALT_SHIFT),62 // CHECKSTYLE.ON: LineLength63 true, "overpassdownload/download", true);64 putValue("help", ht("/Action/OverpassDownload"));65 }66 67 @Override68 public void actionPerformed(ActionEvent e) {69 OverpassDownloadDialog dialog = OverpassDownloadDialog.getInstance();70 dialog.restoreSettings();71 dialog.setVisible(true);72 73 if (dialog.isCanceled()) {74 return;75 }76 77 dialog.rememberSettings();78 Optional<Bounds> selectedArea = dialog.getSelectedDownloadArea();79 String overpassQuery = dialog.getRepairedOverpassQuery();80 81 /*82 * Absence of the selected area can be justified only if the overpass query83 * is not restricted to bbox.84 */85 if (!selectedArea.isPresent() && overpassQuery.contains("{{bbox}}")) {86 JOptionPane.showMessageDialog(87 dialog,88 tr("Please select a download area first."),89 tr("Error"),90 JOptionPane.ERROR_MESSAGE91 );92 return;93 }94 95 /*96 * A callback that is passed to PostDownloadReporter that is called once the download task97 * has finished. According to the number of errors happened, their type we decide whether we98 * want to save the last query in OverpassQueryList.99 */100 Consumer<Collection<Object>> errorReporter = errors -> {101 102 boolean onlyNoDataError = errors.size() == 1 &&103 errors.contains("No data found in this area.");104 105 if (errors.isEmpty() || onlyNoDataError) {106 dialog.saveHistoricItemOnSuccess(overpassQuery);107 }108 };109 110 /*111 * In order to support queries generated by the Overpass Turbo Query Wizard tool112 * which do not require the area to be specified.113 */114 Bounds area = selectedArea.orElseGet(() -> new Bounds(0, 0, 0, 0));115 DownloadOsmTask task = new DownloadOsmTask();116 task.setZoomAfterDownload(dialog.isZoomToDownloadedDataRequired());117 Future<?> future = task.download(118 new OverpassDownloadReader(area, OverpassServerPreference.getOverpassServer(), overpassQuery),119 dialog.isNewLayerRequired(), area, null);120 Main.worker.submit(new PostDownloadHandler(task, future, errorReporter));121 }122 123 private static final class DisableActionsFocusListener implements FocusListener {124 125 private final ActionMap actionMap;126 127 private DisableActionsFocusListener(ActionMap actionMap) {128 this.actionMap = actionMap;129 }130 131 @Override132 public void focusGained(FocusEvent e) {133 enableActions(false);134 }135 136 @Override137 public void focusLost(FocusEvent e) {138 enableActions(true);139 }140 141 private void enableActions(boolean enabled) {142 Object[] allKeys = actionMap.allKeys();143 if (allKeys != null) {144 for (Object key : allKeys) {145 Action action = actionMap.get(key);146 if (action != null) {147 action.setEnabled(enabled);148 }149 }150 }151 }152 }153 154 /**155 * The download dialog that overpass uses.156 * @since 12576 public157 */158 public static final class OverpassDownloadDialog extends DownloadDialog {159 160 private JosmTextArea overpassQuery;161 private OverpassQueryList overpassQueryList;162 private static OverpassDownloadDialog instance;163 private static final BooleanProperty OVERPASS_QUERY_LIST_OPENED =164 new BooleanProperty("download.overpass.query-list.opened", false);165 private static final String ACTION_IMG_SUBDIR = "dialogs";166 167 private OverpassDownloadDialog(Component parent) {168 super(parent, ht("/Action/OverpassDownload"));169 cbDownloadOsmData.setEnabled(false);170 cbDownloadOsmData.setSelected(false);171 cbDownloadGpxData.setVisible(false);172 cbDownloadNotes.setVisible(false);173 cbStartup.setVisible(false);174 }175 176 public static OverpassDownloadDialog getInstance() {177 if (instance == null) {178 instance = new OverpassDownloadDialog(Main.parent);179 }180 return instance;181 }182 183 @Override184 protected void buildMainPanelAboveDownloadSelections(JPanel pnl) {185 DisableActionsFocusListener disableActionsFocusListener =186 new DisableActionsFocusListener(slippyMapChooser.getNavigationComponentActionMap());187 188 String tooltip = tr("Build an Overpass query using the Overpass Turbo Query Wizard tool");189 Action queryWizardAction = new AbstractAction() {190 @Override191 public void actionPerformed(ActionEvent e) {192 new OverpassQueryWizardDialog(instance).showDialog();193 }194 };195 196 JButton openQueryWizard = new JButton(tr("Query Wizard"));197 openQueryWizard.setToolTipText(tooltip);198 openQueryWizard.addActionListener(queryWizardAction);199 200 // use eol() that is needed for the invisible checkboxes cbDownloadGpxData, cbDownloadNotes201 pnl.add(openQueryWizard, GBC.eol());202 pnl.add(new JLabel(tr("Overpass query:")), GBC.std().insets(5, 5, 0, 0).anchor(GBC.NORTHWEST));203 204 // CHECKSTYLE.OFF: LineLength205 this.overpassQuery = new JosmTextArea(206 "/*\n" +207 tr("Place your Overpass query below or generate one using the Overpass Turbo Query Wizard")208 + "\n*/",209 8, 80);210 // CHECKSTYLE.ON: LineLength211 this.overpassQuery.setFont(GuiHelper.getMonospacedFont(overpassQuery));212 this.overpassQuery.addFocusListener(disableActionsFocusListener);213 this.overpassQuery.addFocusListener(new FocusListener() {214 @Override215 public void focusGained(FocusEvent e) {216 overpassQuery.selectAll();217 }218 219 @Override220 public void focusLost(FocusEvent e) {221 // ignored222 }223 });224 225 226 this.overpassQueryList = new OverpassQueryList(this, this.overpassQuery);227 this.overpassQueryList.setPreferredSize(new Dimension(350, 300));228 229 EditSnippetAction edit = new EditSnippetAction();230 RemoveSnippetAction remove = new RemoveSnippetAction();231 this.overpassQueryList.addSelectionListener(edit);232 this.overpassQueryList.addSelectionListener(remove);233 234 JPanel listPanel = new JPanel(new GridBagLayout());235 listPanel.add(new JLabel(tr("Your saved queries:")), GBC.eol().insets(2).anchor(GBC.CENTER));236 listPanel.add(this.overpassQueryList, GBC.eol().fill(GBC.BOTH));237 listPanel.add(new JButton(new AddSnippetAction()), GBC.std().fill(GBC.HORIZONTAL));238 listPanel.add(new JButton(edit), GBC.std().fill(GBC.HORIZONTAL));239 listPanel.add(new JButton(remove), GBC.std().fill(GBC.HORIZONTAL));240 listPanel.setVisible(OVERPASS_QUERY_LIST_OPENED.get());241 242 JScrollPane scrollPane = new JScrollPane(overpassQuery);243 BasicArrowButton arrowButton = new BasicArrowButton(listPanel.isVisible()244 ? BasicArrowButton.EAST245 : BasicArrowButton.WEST);246 arrowButton.setToolTipText(tr("Show/hide Overpass snippet list"));247 arrowButton.addActionListener(e -> {248 if (listPanel.isVisible()) {249 listPanel.setVisible(false);250 arrowButton.setDirection(BasicArrowButton.WEST);251 OVERPASS_QUERY_LIST_OPENED.put(Boolean.FALSE);252 } else {253 listPanel.setVisible(true);254 arrowButton.setDirection(BasicArrowButton.EAST);255 OVERPASS_QUERY_LIST_OPENED.put(Boolean.TRUE);256 }257 });258 259 JPanel innerPanel = new JPanel(new BorderLayout());260 innerPanel.add(scrollPane, BorderLayout.CENTER);261 innerPanel.add(arrowButton, BorderLayout.EAST);262 263 JPanel pane = new JPanel(new BorderLayout());264 pane.add(innerPanel, BorderLayout.CENTER);265 pane.add(listPanel, BorderLayout.EAST);266 267 GBC gbc = GBC.eol().fill(GBC.HORIZONTAL); gbc.ipady = 200;268 pnl.add(pane, gbc);269 }270 271 public String getOverpassQuery() {272 return overpassQuery.getText();273 }274 275 String getRepairedOverpassQuery() {276 String query = getOverpassQuery();277 if (query.matches("(/\\*(\\*[^/]|[^\\*/])*\\*/|\\s)*")) {278 // Empty query. User might want to download everything279 boolean doFix = ConditionalOptionPaneUtil.showConfirmationDialog(280 "download.overpass.fix.emptytoall",281 this,282 tr("You entered an empty query. Do you want to download all data in this area instead?"),283 tr("Download all data?"),284 JOptionPane.YES_NO_OPTION,285 JOptionPane.QUESTION_MESSAGE,286 JOptionPane.YES_OPTION);287 if (doFix) {288 return "[out:xml]; \n"289 + query + "\n"290 + "(\n"291 + " node({{bbox}});\n"292 + "<;\n"293 + ");\n"294 + "(._;>;);"295 + "out meta;";296 }297 }298 // Note: We can add more repairs here. We might e.g. want to intercept missing 'out meta'.299 return query;300 }301 302 /**303 * Sets the query that is displayed304 * @param text The multiline query text.305 * @since 12576 public306 */307 public void setOverpassQuery(String text) {308 overpassQuery.setText(text);309 }310 311 /**312 * Adds the current query to {@link OverpassQueryList}.313 * @param overpassQueryToSave The query to save314 */315 void saveHistoricItemOnSuccess(String overpassQueryToSave) {316 overpassQueryList.saveHistoricItem(overpassQueryToSave);317 }318 319 @Override320 protected void updateSizeCheck() {321 displaySizeCheckResult(false);322 }323 324 /**325 * Triggers the download action to fire.326 * @since 12576 public327 */328 public void triggerDownload() {329 super.btnDownload.doClick();330 }331 332 /**333 * Action that delegates snippet creation to {@link OverpassQueryList#createNewItem()}.334 */335 class AddSnippetAction extends AbstractAction {336 337 /**338 * Constructs a new {@code AddSnippetAction}.339 */340 AddSnippetAction() {341 super();342 putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "add"));343 putValue(SHORT_DESCRIPTION, tr("Add new snippet"));344 }345 346 @Override347 public void actionPerformed(ActionEvent e) {348 overpassQueryList.createNewItem();349 }350 }351 352 /**353 * Action that delegates snippet removal to {@link OverpassQueryList#removeSelectedItem()}.354 */355 class RemoveSnippetAction extends AbstractAction implements ListSelectionListener {356 357 /**358 * Constructs a new {@code RemoveSnippetAction}.359 */360 RemoveSnippetAction() {361 super();362 putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "delete"));363 putValue(SHORT_DESCRIPTION, tr("Delete selected snippet"));364 checkEnabled();365 }366 367 @Override368 public void actionPerformed(ActionEvent e) {369 overpassQueryList.removeSelectedItem();370 }371 372 /**373 * Disables the action if no items are selected.374 */375 void checkEnabled() {376 setEnabled(overpassQueryList.getSelectedItem().isPresent());377 }378 379 @Override380 public void valueChanged(ListSelectionEvent e) {381 checkEnabled();382 }383 }384 385 /**386 * Action that delegates snippet edit to {@link OverpassQueryList#editSelectedItem()}.387 */388 class EditSnippetAction extends AbstractAction implements ListSelectionListener {389 390 /**391 * Constructs a new {@code EditSnippetAction}.392 */393 EditSnippetAction() {394 super();395 putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "edit"));396 putValue(SHORT_DESCRIPTION, tr("Edit selected snippet"));397 checkEnabled();398 }399 400 @Override401 public void actionPerformed(ActionEvent e) {402 overpassQueryList.editSelectedItem();403 }404 405 /**406 * Disables the action if no items are selected.407 */408 void checkEnabled() {409 setEnabled(overpassQueryList.getSelectedItem().isPresent());410 }411 412 @Override413 public void valueChanged(ListSelectionEvent e) {414 checkEnabled();415 }416 }417 }418 } -
src/org/openstreetmap/josm/gui/MainMenu.java
72 72 import org.openstreetmap.josm.actions.OpenLocationAction; 73 73 import org.openstreetmap.josm.actions.OrthogonalizeAction; 74 74 import org.openstreetmap.josm.actions.OrthogonalizeAction.Undo; 75 import org.openstreetmap.josm.actions.OverpassDownloadAction;76 75 import org.openstreetmap.josm.actions.PasteAction; 77 76 import org.openstreetmap.josm.actions.PasteAtSourcePositionAction; 78 77 import org.openstreetmap.josm.actions.PasteTagsAction; … … 159 158 public final DownloadAction download = new DownloadAction(); 160 159 /** File / Download in current view **/ 161 160 public final DownloadOsmInViewAction downloadInView = new DownloadOsmInViewAction(); 162 /** File / Download from Overpass API... **/163 public final OverpassDownloadAction overpassDownload = new OverpassDownloadAction();164 161 /** File / Download object... **/ 165 162 public final DownloadPrimitiveAction downloadPrimitive = new DownloadPrimitiveAction(); 166 163 /** File / Download notes in current view **/ … … 657 654 fileMenu.addSeparator(); 658 655 add(fileMenu, download); 659 656 add(fileMenu, downloadInView, true); 660 add(fileMenu, overpassDownload, true);661 657 add(fileMenu, downloadPrimitive); 662 658 add(fileMenu, searchNotes); 663 659 add(fileMenu, downloadNotesInView); -
src/org/openstreetmap/josm/gui/download/AbstractDownloadSourcePanel.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.download; 3 4 import org.openstreetmap.josm.data.Bounds; 5 6 import javax.swing.Icon; 7 import javax.swing.JPanel; 8 import java.util.Objects; 9 10 /** 11 * GUI representation of {@link DownloadSource} that is shown to the user in 12 * {@link DownloadDialog}. 13 * @param <T> The type of the data that a download source uses. 14 */ 15 public abstract class AbstractDownloadSourcePanel<T> extends JPanel { 16 17 public AbstractDownloadSourcePanel(final DownloadSource<T> downloadSource) { 18 Objects.requireNonNull(downloadSource); 19 this.downloadSource = downloadSource; 20 } 21 22 /** 23 * The download source of this panel. 24 */ 25 protected transient DownloadSource<T> downloadSource; 26 27 /** 28 * Gets the data. 29 * @return Returns the data. 30 */ 31 public abstract T getData(); 32 33 /** 34 * Gets the download source of this panel. 35 * @return Returns the download source of this panel. 36 */ 37 public DownloadSource<T> getDownloadSource() { 38 return this.downloadSource; 39 } 40 41 /** 42 * Saves the current user preferences devoted to the data source. 43 */ 44 public abstract void rememberSettings(); 45 46 /** 47 * Restores the latest user preferences devoted to the data source. 48 */ 49 public abstract void restoreSettings(); 50 51 /** 52 * Performs the logic needed in case if the user triggered the download 53 * action in {@link DownloadDialog}. 54 * @return Returns {@code true} if the required procedure of handling the 55 * download action succeeded and {@link DownloadDialog} can be closed, e.g. validation, 56 * otherwise {@code false}. 57 */ 58 public abstract boolean checkDownload(Bounds bbox, DownloadSettings settings); 59 60 /** 61 * Performs the logic needed in case if the user triggered the cancel 62 * action in {@link DownloadDialog}. 63 */ 64 public void checkCancel() { 65 // nothing, let download dialog to close 66 // override if necessary 67 } 68 69 /** 70 * Gets the icon of the download source panel. 71 * @return The icon. Can be {@code null} if there is no icon associated with 72 * this download source. 73 */ 74 public Icon getIcon() { 75 return null; 76 } 77 78 /** 79 * Updates GUI components of the panel according to the bbox changes. 80 * @param bbox The new value for the bounding box. 81 */ 82 public void boudingBoxChanged(Bounds bbox) { 83 // override this if the panel must react on bbox changes 84 } 85 } -
src/org/openstreetmap/josm/gui/download/DownloadDialog.java
5 5 import static org.openstreetmap.josm.tools.I18n.tr; 6 6 7 7 import java.awt.BorderLayout; 8 import java.awt.Color;9 8 import java.awt.Component; 10 9 import java.awt.Dimension; 11 10 import java.awt.FlowLayout; 12 import java.awt.Font;13 11 import java.awt.Graphics; 14 12 import java.awt.GridBagLayout; 15 13 import java.awt.event.ActionEvent; … … 18 16 import java.awt.event.WindowAdapter; 19 17 import java.awt.event.WindowEvent; 20 18 import java.util.ArrayList; 19 import java.util.Arrays; 21 20 import java.util.List; 22 21 import java.util.Optional; 22 import java.util.stream.IntStream; 23 23 24 24 import javax.swing.AbstractAction; 25 import javax.swing.Icon; 25 26 import javax.swing.JButton; 26 27 import javax.swing.JCheckBox; 27 28 import javax.swing.JComponent; 28 29 import javax.swing.JDialog; 29 30 import javax.swing.JLabel; 30 import javax.swing.JOptionPane;31 31 import javax.swing.JPanel; 32 import javax.swing.JSplitPane; 32 33 import javax.swing.JTabbedPane; 33 34 import javax.swing.KeyStroke; 34 import javax.swing.event.ChangeListener;35 35 36 36 import org.openstreetmap.josm.Main; 37 37 import org.openstreetmap.josm.actions.ExpertToggleAction; … … 49 49 import org.openstreetmap.josm.tools.ImageProvider; 50 50 import org.openstreetmap.josm.tools.InputMapUtils; 51 51 import org.openstreetmap.josm.tools.OsmUrlToBounds; 52 import org.openstreetmap.josm.tools.Utils;53 52 import org.openstreetmap.josm.tools.WindowGeometry; 54 53 55 54 /** 56 * Dialog displayed to download OSM and/or GPS data from OSM server.55 * Dialog displayed to the user to download mapping data. 57 56 */ 58 57 public class DownloadDialog extends JDialog { 58 59 /** 60 * Preference properties 61 */ 59 62 private static final IntegerProperty DOWNLOAD_TAB = new IntegerProperty("download.tab", 0); 60 63 private static final IntegerProperty DOWNLOAD_SOURCE_TAB = new IntegerProperty("download-source.tab", 0); 64 private static final IntegerProperty DIALOG_SPLIT = new IntegerProperty("download.split", 200); 61 65 private static final BooleanProperty DOWNLOAD_AUTORUN = new BooleanProperty("download.autorun", false); 62 private static final BooleanProperty DOWNLOAD_OSM = new BooleanProperty("download.osm", true);63 private static final BooleanProperty DOWNLOAD_GPS = new BooleanProperty("download.gps", false);64 private static final BooleanProperty DOWNLOAD_NOTES = new BooleanProperty("download.notes", false);65 66 private static final BooleanProperty DOWNLOAD_NEWLAYER = new BooleanProperty("download.newlayer", false); 66 67 private static final BooleanProperty DOWNLOAD_ZOOMTODATA = new BooleanProperty("download.zoomtodata", true); 67 68 … … 80 81 return instance; 81 82 } 82 83 83 protected SlippyMapChooser slippyMapChooser;84 protected final transient List<DownloadSource> downloadSources = new ArrayList<>(); 84 85 protected final transient List<DownloadSelection> downloadSelections = new ArrayList<>(); 85 86 protected final JTabbedPane tpDownloadAreaSelectors = new JTabbedPane(); 87 protected final JTabbedPane downloadSourcesTab = new JTabbedPane(); 88 86 89 protected JCheckBox cbNewLayer; 87 90 protected JCheckBox cbStartup; 88 91 protected JCheckBox cbZoomToDownloadedData; 89 protected final JLabel sizeCheck = new JLabel(); 92 protected SlippyMapChooser slippyMapChooser; 93 protected JPanel mainPanel; 94 protected JSplitPane dialogSplit; 95 96 /* 97 * Keep the reference globally to avoid having it garbage collected 98 */ 99 protected final transient ExpertToggleAction.ExpertModeChangeListener expertListener = 100 getExpertModeListenerForDownloadSources(); 90 101 protected transient Bounds currentBounds; 91 102 protected boolean canceled; 92 103 93 protected JCheckBox cbDownloadOsmData; 94 protected JCheckBox cbDownloadGpxData; 95 protected JCheckBox cbDownloadNotes; 96 /** the download action and button */ 97 private final DownloadAction actDownload = new DownloadAction(); 98 protected final JButton btnDownload = new JButton(actDownload); 104 protected JButton btnDownload; 105 protected JButton btnCancel; 106 protected JButton btnHelp; 99 107 108 /** 109 * Builds the main panel of the dialog. 110 * @return The panel of the dialog. 111 */ 100 112 protected final JPanel buildMainPanel() { 101 JPanel pnl = new JPanel(new GridBagLayout());113 mainPanel = new JPanel(new GridBagLayout()); 102 114 103 // size check depends on selected data source104 final ChangeListener checkboxChangeListener = e -> updateSizeCheck();115 downloadSources.add(new OSMDownloadSource()); 116 downloadSources.add(new OverpassDownloadSource()); 105 117 106 // adding the download tasks 107 pnl.add(new JLabel(tr("Data Sources and Types:")), GBC.std().insets(5, 5, 1, 5)); 108 cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true); 109 cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area.")); 110 cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener); 111 pnl.add(cbDownloadOsmData, GBC.std().insets(1, 5, 1, 5)); 112 cbDownloadGpxData = new JCheckBox(tr("Raw GPS data")); 113 cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area.")); 114 cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener); 115 pnl.add(cbDownloadGpxData, GBC.std().insets(5, 5, 1, 5)); 116 cbDownloadNotes = new JCheckBox(tr("Notes")); 117 cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area.")); 118 cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener); 119 pnl.add(cbDownloadNotes, GBC.eol().insets(50, 5, 1, 5)); 118 // register all default download sources 119 for (int i = 0; i < downloadSources.size(); i++) { 120 downloadSources.get(i).addGui(this); 121 } 120 122 121 123 // must be created before hook 122 124 slippyMapChooser = new SlippyMapChooser(); 123 125 124 // hook for subclasses125 buildMainPanelAboveDownloadSelections(pnl);126 127 126 // predefined download selections 128 127 downloadSelections.add(slippyMapChooser); 129 128 downloadSelections.add(new BookmarkSelection()); … … 134 133 // add selections from plugins 135 134 PluginHandler.addDownloadSelection(downloadSelections); 136 135 137 // now everybody may add their tab to the tabbed pane 138 // (not done right away to allow plugins to remove one of 139 // the default selectors!) 140 for (DownloadSelection s : downloadSelections) { 141 s.addGui(this); 136 // register all default download selections 137 for (int i = 0; i < downloadSelections.size(); i++) { 138 downloadSelections.get(i).addGui(this); 142 139 } 143 140 144 pnl.add(tpDownloadAreaSelectors, GBC.eol().fill()); 141 // allow to collapse the panes completely 142 downloadSourcesTab.setMinimumSize(new Dimension(0, 0)); 143 tpDownloadAreaSelectors.setMinimumSize(new Dimension(0, 0)); 145 144 146 try { 147 tpDownloadAreaSelectors.setSelectedIndex(DOWNLOAD_TAB.get()); 148 } catch (IndexOutOfBoundsException ex) { 149 Main.trace(ex); 150 DOWNLOAD_TAB.put(0); 151 } 145 dialogSplit = new JSplitPane( 146 JSplitPane.VERTICAL_SPLIT, 147 downloadSourcesTab, 148 tpDownloadAreaSelectors); 152 149 153 Font labelFont = sizeCheck.getFont(); 154 sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize())); 150 mainPanel.add(dialogSplit, GBC.eol().fill()); 155 151 156 152 cbNewLayer = new JCheckBox(tr("Download as new layer")); 157 153 cbNewLayer.setToolTipText(tr("<html>Select to download data into a new data layer.<br>" … … 166 162 cbZoomToDownloadedData = new JCheckBox(tr("Zoom to downloaded data")); 167 163 cbZoomToDownloadedData.setToolTipText(tr("Select to zoom to entire newly downloaded data.")); 168 164 169 pnl.add(cbNewLayer, GBC.std().anchor(GBC.WEST).insets(5, 5, 5, 5));170 pnl.add(cbStartup, GBC.std().anchor(GBC.WEST).insets(15, 5, 5, 5));171 pnl.add(cbZoomToDownloadedData, GBC.std().anchor(GBC.WEST).insets(15, 5, 5, 5));165 mainPanel.add(cbNewLayer, GBC.std().anchor(GBC.WEST).insets(5, 5, 5, 5)); 166 mainPanel.add(cbStartup, GBC.std().anchor(GBC.WEST).insets(15, 5, 5, 5)); 167 mainPanel.add(cbZoomToDownloadedData, GBC.std().anchor(GBC.WEST).insets(15, 5, 5, 5)); 172 168 173 169 ExpertToggleAction.addVisibilitySwitcher(cbZoomToDownloadedData); 174 170 175 pnl.add(sizeCheck, GBC.eol().anchor(GBC.EAST).insets(5, 5, 5, 2));176 177 171 if (!ExpertToggleAction.isExpert()) { 178 172 JLabel infoLabel = new JLabel( 179 173 tr("Use left click&drag to select area, arrows or right mouse button to scroll map, wheel or +/- to zoom.")); 180 pnl.add(infoLabel, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 0, 0));174 mainPanel.add(infoLabel, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 0, 0)); 181 175 } 182 return pnl;176 return mainPanel; 183 177 } 184 178 185 179 /* This should not be necessary, but if not here, repaint is not always correct in SlippyMap! */ … … 189 183 super.paint(g); 190 184 } 191 185 186 /** 187 * Builds the button pane of the dialog. 188 * @return The button panel of the dialog. 189 */ 192 190 protected final JPanel buildButtonPanel() { 191 btnDownload = new JButton(new DownloadAction()); 192 btnCancel = new JButton(new CancelAction()); 193 btnHelp = new JButton( 194 new ContextSensitiveHelpAction(getRootPane().getClientProperty("help").toString())); 195 193 196 JPanel pnl = new JPanel(new FlowLayout()); 194 197 195 // -- download button196 198 pnl.add(btnDownload); 199 pnl.add(btnCancel); 200 pnl.add(btnHelp); 201 197 202 InputMapUtils.enableEnter(btnDownload); 198 199 InputMapUtils.addEnterActionWhenAncestor(cbDownloadGpxData, actDownload);200 InputMapUtils.addEnterActionWhenAncestor(cbDownloadOsmData, actDownload);201 InputMapUtils.addEnterActionWhenAncestor(cbDownloadNotes, actDownload);202 InputMapUtils.addEnterActionWhenAncestor(cbNewLayer, actDownload);203 InputMapUtils.addEnterActionWhenAncestor(cbStartup, actDownload);204 InputMapUtils.addEnterActionWhenAncestor(cbZoomToDownloadedData, actDownload);205 206 // -- cancel button207 JButton btnCancel;208 CancelAction actCancel = new CancelAction();209 btnCancel = new JButton(actCancel);210 pnl.add(btnCancel);211 203 InputMapUtils.enableEnter(btnCancel); 204 InputMapUtils.addEscapeAction(getRootPane(), btnCancel.getAction()); 205 InputMapUtils.enableEnter(btnHelp); 212 206 213 // -- cancel on ESC 214 InputMapUtils.addEscapeAction(getRootPane(), actCancel); 207 InputMapUtils.addEnterActionWhenAncestor(cbNewLayer, btnDownload.getAction()); 208 InputMapUtils.addEnterActionWhenAncestor(cbStartup, btnDownload.getAction()); 209 InputMapUtils.addEnterActionWhenAncestor(cbZoomToDownloadedData, btnDownload.getAction()); 215 210 216 // -- help button217 JButton btnHelp = new JButton(new ContextSensitiveHelpAction(getRootPane().getClientProperty("help").toString()));218 pnl.add(btnHelp);219 InputMapUtils.enableEnter(btnHelp);220 221 211 return pnl; 222 212 } 223 213 … … 258 248 } 259 249 }); 260 250 addWindowListener(new WindowEventHandler()); 251 ExpertToggleAction.addExpertModeChangeListener(expertListener); 261 252 restoreSettings(); 262 253 } 263 254 264 protected void updateSizeCheck() {265 boolean isAreaTooLarge = false;266 if (currentBounds == null) {267 sizeCheck.setText(tr("No area selected yet"));268 sizeCheck.setForeground(Color.darkGray);269 } else if (isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) {270 // see max_note_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml271 isAreaTooLarge = currentBounds.getArea() > Main.pref.getDouble("osm-server.max-request-area-notes", 25);272 } else {273 // see max_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml274 isAreaTooLarge = currentBounds.getArea() > Main.pref.getDouble("osm-server.max-request-area", 0.25);275 }276 displaySizeCheckResult(isAreaTooLarge);277 }278 279 protected void displaySizeCheckResult(boolean isAreaTooLarge) {280 if (isAreaTooLarge) {281 sizeCheck.setText(tr("Download area too large; will probably be rejected by server"));282 sizeCheck.setForeground(Color.red);283 } else {284 sizeCheck.setText(tr("Download area ok, size probably acceptable to server"));285 sizeCheck.setForeground(Color.darkGray);286 }287 }288 289 255 /** 290 256 * Distributes a "bounding box changed" from one DownloadSelection 291 * object to the others, so they may update or clear their input fields. 257 * object to the others, so they may update or clear their input fields. Also informs 258 * download sources about the change, so they can react on it. 292 259 * @param b new current bounds 293 260 * 294 261 * @param eventSource - the DownloadSelection object that fired this notification. … … 300 267 s.setDownloadArea(currentBounds); 301 268 } 302 269 } 303 updateSizeCheck(); 270 271 for (Component ds : downloadSourcesTab.getComponents()) { 272 if (ds instanceof AbstractDownloadSourcePanel) { 273 ((AbstractDownloadSourcePanel) ds).boudingBoxChanged(b); 274 } 275 } 304 276 } 305 277 306 278 /** … … 309 281 */ 310 282 public void startDownload(Bounds b) { 311 283 this.currentBounds = b; 312 actDownload.run();284 startDownload(); 313 285 } 314 286 315 287 /** 316 * Replies true if the user selected to download OSM data 317 * 318 * @return true if the user selected to download OSM data 288 * Starts download. 319 289 */ 320 public boolean isDownloadOsmData() {321 return cbDownloadOsmData.isSelected();290 public void startDownload() { 291 btnDownload.doClick(); 322 292 } 323 293 324 294 /** 325 * Replies true if the user selected to download GPX data326 *327 * @return true if the user selected to download GPX data328 */329 public boolean isDownloadGpxData() {330 return cbDownloadGpxData.isSelected();331 }332 333 /**334 * Replies true if user selected to download notes335 *336 * @return true if user selected to download notes337 */338 public boolean isDownloadNotes() {339 return cbDownloadNotes.isSelected();340 }341 342 /**343 295 * Replies true if the user requires to download into a new layer 344 296 * 345 297 * @return true if the user requires to download into a new layer … … 359 311 } 360 312 361 313 /** 314 * Determines if the dialog autorun is enabled in preferences. 315 * @return {@code true} if the download dialog must be open at startup, {@code false} otherwise 316 */ 317 public static boolean isAutorunEnabled() { 318 return DOWNLOAD_AUTORUN.get(); 319 } 320 321 /** 362 322 * Adds a new download area selector to the download dialog 363 323 * 364 324 * @param selector the download are selector … … 369 329 } 370 330 371 331 /** 332 * Adds a new download source to the download dialog 333 * 334 * @param downloadSource The download source to be added. 335 * @param <T> The type of the download data. 336 */ 337 public <T> void addDownloadSource(DownloadSource<T> downloadSource) { 338 if ((ExpertToggleAction.isExpert() && downloadSource.onlyExpert()) || !downloadSource.onlyExpert()) { 339 addNewDownloadSourceTab(downloadSource); 340 } 341 } 342 343 /** 372 344 * Refreshes the tile sources 373 345 * @since 6364 374 346 */ … … 383 355 */ 384 356 public void rememberSettings() { 385 357 DOWNLOAD_TAB.put(tpDownloadAreaSelectors.getSelectedIndex()); 386 DOWNLOAD_OSM.put(cbDownloadOsmData.isSelected()); 387 DOWNLOAD_GPS.put(cbDownloadGpxData.isSelected()); 388 DOWNLOAD_NOTES.put(cbDownloadNotes.isSelected()); 358 DOWNLOAD_SOURCE_TAB.put(downloadSourcesTab.getSelectedIndex()); 359 DIALOG_SPLIT.put(dialogSplit.getDividerLocation()); 389 360 DOWNLOAD_NEWLAYER.put(cbNewLayer.isSelected()); 390 361 DOWNLOAD_ZOOMTODATA.put(cbZoomToDownloadedData.isSelected()); 391 362 if (currentBounds != null) { … … 397 368 * Restores the previous settings in the download dialog. 398 369 */ 399 370 public void restoreSettings() { 400 cbDownloadOsmData.setSelected(DOWNLOAD_OSM.get());401 cbDownloadGpxData.setSelected(DOWNLOAD_GPS.get());402 cbDownloadNotes.setSelected(DOWNLOAD_NOTES.get());403 371 cbNewLayer.setSelected(DOWNLOAD_NEWLAYER.get()); 404 372 cbStartup.setSelected(isAutorunEnabled()); 405 373 cbZoomToDownloadedData.setSelected(DOWNLOAD_ZOOMTODATA.get()); 406 int idx = Utils.clamp(DOWNLOAD_TAB.get(), 0, tpDownloadAreaSelectors.getTabCount() - 1); 407 tpDownloadAreaSelectors.setSelectedIndex(idx); 374 dialogSplit.setDividerLocation(DIALOG_SPLIT.get()); 408 375 376 try { 377 tpDownloadAreaSelectors.setSelectedIndex(DOWNLOAD_TAB.get()); 378 } catch (IndexOutOfBoundsException e) { 379 Main.trace(e); 380 tpDownloadAreaSelectors.setSelectedIndex(0); 381 } 382 383 try { 384 downloadSourcesTab.setSelectedIndex(DOWNLOAD_SOURCE_TAB.get()); 385 } catch (IndexOutOfBoundsException e) { 386 Main.trace(e); 387 downloadSourcesTab.setSelectedIndex(0); 388 } 389 409 390 if (Main.isDisplayingMapView()) { 410 391 MapView mv = Main.map.mapView; 411 392 currentBounds = new Bounds( … … 440 421 } 441 422 442 423 /** 443 * Determines if the dialog autorun is enabled in preferences.444 * @return {@code true} if the download dialog must be open at startup, {@code false} otherwise445 */446 public static boolean isAutorunEnabled() {447 return DOWNLOAD_AUTORUN.get();448 }449 450 /**451 424 * Automatically opens the download dialog, if autorun is enabled. 452 425 * @see #isAutorunEnabled 453 426 */ … … 491 464 return canceled; 492 465 } 493 466 467 /** 468 * Gets the global settings of the download dialog. 469 * @return The {@link DownloadSettings} object that describes the current state of 470 * the download dialog. 471 */ 472 public DownloadSettings getDownloadSettings() { 473 return new DownloadSettings(isNewLayerRequired(), isZoomToDownloadedDataRequired()); 474 } 475 494 476 protected void setCanceled(boolean canceled) { 495 477 this.canceled = canceled; 496 478 } 497 479 498 protected void buildMainPanelAboveDownloadSelections(JPanel pnl) { 499 // Do nothing 480 /** 481 * Returns position of the download source in the tabbed pane. 482 * @param downloadSource The download source. 483 * @return The index of the download source, or -1 if it not in the pane. 484 */ 485 protected int getDownloadSourceIndex(DownloadSource downloadSource) { 486 return Arrays.stream(downloadSourcesTab.getComponents()) 487 .filter(it -> it instanceof AbstractDownloadSourcePanel) 488 .map(it -> (AbstractDownloadSourcePanel) it) 489 .filter(it -> it.getDownloadSource().equals(downloadSource)) 490 .findAny() 491 .map(downloadSourcesTab::indexOfComponent) 492 .orElse(-1); 500 493 } 501 494 495 /** 496 * Adds the download source to the download sources tab. 497 * @param downloadSource The download source to be added. 498 * @param <T> The type of the download data. 499 */ 500 private <T> void addNewDownloadSourceTab(DownloadSource<T> downloadSource) { 501 AbstractDownloadSourcePanel<T> panel = downloadSource.createPanel(); 502 downloadSourcesTab.add(panel, downloadSource.getLabel()); 503 Icon icon = panel.getIcon(); 504 if (icon != null) { 505 int idx = getDownloadSourceIndex(downloadSource); 506 downloadSourcesTab.setIconAt( 507 idx != -1 ? idx : downloadSourcesTab.getTabCount() - 1, 508 icon); 509 } 510 } 511 512 /** 513 * Creates listener that removes/adds download sources from/to {@code downloadSourcesTab} 514 * depending on the current mode. 515 * @return The expert mode listener. 516 */ 517 private ExpertToggleAction.ExpertModeChangeListener getExpertModeListenerForDownloadSources() { 518 return isExpert -> { 519 if (isExpert) { 520 downloadSources.stream() 521 .filter(DownloadSource::onlyExpert) 522 .filter(it -> getDownloadSourceIndex(it) == -1) 523 .forEach(this::addNewDownloadSourceTab); 524 } else { 525 IntStream.range(0, downloadSourcesTab.getTabCount()) 526 .mapToObj(downloadSourcesTab::getComponentAt) 527 .filter(it -> it instanceof AbstractDownloadSourcePanel) 528 .map(it -> (AbstractDownloadSourcePanel) it) 529 .filter(it -> it.getDownloadSource().onlyExpert()) 530 .forEach(downloadSourcesTab::remove); 531 } 532 }; 533 } 534 535 /** 536 * Action that is executed when the cancel button is pressed. 537 */ 502 538 class CancelAction extends AbstractAction { 503 539 CancelAction() { 504 540 putValue(NAME, tr("Cancel")); … … 513 549 514 550 @Override 515 551 public void actionPerformed(ActionEvent e) { 552 AbstractDownloadSourcePanel pnl = (AbstractDownloadSourcePanel) downloadSourcesTab.getSelectedComponent(); 516 553 run(); 554 pnl.checkCancel(); 517 555 } 518 556 } 519 557 558 /** 559 * Action that is executed when the download button is pressed. 560 */ 520 561 class DownloadAction extends AbstractAction { 521 562 DownloadAction() { 522 563 putValue(NAME, tr("Download")); … … 526 567 } 527 568 528 569 public void run() { 529 /* 530 * Checks if the user selected the type of data to download. At least one the following 531 * must be chosen : raw osm data, gpx data, notes. 532 * If none of those are selected, then the corresponding dialog is shown to inform the user. 533 */ 534 if (!isDownloadOsmData() && !isDownloadGpxData() && !isDownloadNotes()) { 535 JOptionPane.showMessageDialog( 536 DownloadDialog.this, 537 tr("<html>Neither <strong>{0}</strong> nor <strong>{1}</strong> nor <strong>{2}</strong> is enabled.<br>" 538 + "Please choose to either download OSM data, or GPX data, or Notes, or all.</html>", 539 cbDownloadOsmData.getText(), 540 cbDownloadGpxData.getText(), 541 cbDownloadNotes.getText() 542 ), 543 tr("Error"), 544 JOptionPane.ERROR_MESSAGE 545 ); 546 return; 570 Component panel = downloadSourcesTab.getSelectedComponent(); 571 if (panel instanceof AbstractDownloadSourcePanel) { 572 AbstractDownloadSourcePanel pnl = (AbstractDownloadSourcePanel) panel; 573 DownloadSettings downloadSettings = getDownloadSettings(); 574 if (pnl.checkDownload(currentBounds, downloadSettings)) { 575 rememberSettings(); 576 setCanceled(false); 577 setVisible(false); 578 pnl.getDownloadSource().doDownload(currentBounds, pnl.getData(), downloadSettings); 579 } 547 580 } 548 549 setCanceled(false);550 setVisible(false);551 581 } 552 582 553 583 @Override -
src/org/openstreetmap/josm/gui/download/DownloadSettings.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.download; 3 4 /** 5 * The global settings of {@link DownloadDialog}. 6 */ 7 public final class DownloadSettings { 8 9 private boolean downloadAsNewLayer; 10 private boolean zoomToDownloadedData; 11 12 /** 13 * Initializes a new instance of {@code DownloadSettings}. 14 * @param downloadAsNewLayer The flag defining if a new layer must be created for the downloaded data. 15 * @param zoomToDownloadedData The flag defining if the map view, see {@link SlippyMapChooser}, 16 * must zoom to the downloaded data. 17 */ 18 public DownloadSettings(boolean downloadAsNewLayer, boolean zoomToDownloadedData) { 19 this.downloadAsNewLayer = downloadAsNewLayer; 20 this.zoomToDownloadedData = zoomToDownloadedData; 21 } 22 23 /** 24 * Gets the flag defining if a new layer must be created for the downloaded data. 25 * @return {@code true} if a new layer must be created, {@code false} otherwise. 26 */ 27 public boolean asNewLayer() { 28 return this.downloadAsNewLayer; 29 } 30 31 /** 32 * Gets the flag defining if the map view must zoom to the downloaded data. 33 * @return {@code true} if the view must zoom, {@code false} otherwise. 34 */ 35 public boolean zoomToData() { 36 return this.zoomToDownloadedData; 37 } 38 } -
src/org/openstreetmap/josm/gui/download/DownloadSource.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.download; 3 4 5 import org.openstreetmap.josm.data.Bounds; 6 7 /** 8 * Defines an interface for different download sources. 9 * @param <T> The type of the data that a download source uses. 10 */ 11 public interface DownloadSource<T> { 12 13 /** 14 * Creates a panel with GUI specific for the download source. 15 * @return Returns {@link AbstractDownloadSourcePanel}. 16 */ 17 AbstractDownloadSourcePanel<T> createPanel(); 18 19 /** 20 * Downloads the data. 21 * @param bbox The bounding box. Can be null if no bounding box selected. 22 * @param data The required data for the download source. 23 * @param settings The global settings of the download dialog, see {@link DownloadDialog}. 24 */ 25 void doDownload(Bounds bbox, T data, DownloadSettings settings); 26 27 /** 28 * Returns a string representation of this download source. 29 * @return A string representation of this download source. 30 */ 31 String getLabel(); 32 33 /** 34 * Add a download source to the dialog, see {@link DownloadDialog}. 35 * @param dialog The download dialog. 36 */ 37 void addGui(DownloadDialog dialog); 38 39 /** 40 * Defines whether this download source should be visible only in the expert mode. 41 * @return Returns {@code true} if the download source should be visible only in the 42 * expert mode, {@code false} otherwise. 43 */ 44 boolean onlyExpert(); 45 } -
src/org/openstreetmap/josm/gui/download/OSMDownloadSource.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.download; 3 4 import org.openstreetmap.josm.Main; 5 import org.openstreetmap.josm.actions.downloadtasks.AbstractDownloadTask; 6 import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask; 7 import org.openstreetmap.josm.actions.downloadtasks.DownloadNotesTask; 8 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask; 9 import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler; 10 import org.openstreetmap.josm.data.Bounds; 11 import org.openstreetmap.josm.data.ProjectionBounds; 12 import org.openstreetmap.josm.data.ViewportData; 13 import org.openstreetmap.josm.data.preferences.BooleanProperty; 14 import org.openstreetmap.josm.gui.util.GuiHelper; 15 import org.openstreetmap.josm.tools.GBC; 16 import org.openstreetmap.josm.tools.ImageProvider; 17 import org.openstreetmap.josm.tools.Pair; 18 19 import javax.swing.Icon; 20 import javax.swing.JCheckBox; 21 import javax.swing.JLabel; 22 import javax.swing.JOptionPane; 23 import javax.swing.event.ChangeListener; 24 25 import java.awt.Color; 26 import java.awt.Font; 27 import java.awt.GridBagLayout; 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.concurrent.ExecutionException; 31 import java.util.concurrent.Future; 32 33 import static org.openstreetmap.josm.tools.I18n.tr; 34 35 /** 36 * Class defines the way data is fetched from the OSM server. 37 */ 38 public class OSMDownloadSource implements DownloadSource<OSMDownloadSource.OSMDownloadData> { 39 40 @Override 41 public AbstractDownloadSourcePanel<OSMDownloadData> createPanel() { 42 return new OSMDownloadSourcePanel(this); 43 } 44 45 @Override 46 public void doDownload(Bounds bbox, OSMDownloadData data, DownloadSettings settings) { 47 boolean zoom = settings.zoomToData(); 48 boolean newLayer = settings.asNewLayer(); 49 List<Pair<AbstractDownloadTask<?>, Future<?>>> tasks = new ArrayList<>(); 50 51 if (data.isDownloadOSMData()) { 52 DownloadOsmTask task = new DownloadOsmTask(); 53 task.setZoomAfterDownload(zoom && !data.isDownloadGPX() && !data.isDownloadNotes()); 54 Future<?> future = task.download(newLayer, bbox, null); 55 Main.worker.submit(new PostDownloadHandler(task, future)); 56 if (zoom) { 57 tasks.add(new Pair<>(task, future)); 58 } 59 } 60 61 if (data.isDownloadGPX()) { 62 DownloadGpsTask task = new DownloadGpsTask(); 63 task.setZoomAfterDownload(zoom && !data.isDownloadOSMData() && !data.isDownloadNotes()); 64 Future<?> future = task.download(newLayer, bbox, null); 65 Main.worker.submit(new PostDownloadHandler(task, future)); 66 if (zoom) { 67 tasks.add(new Pair<>(task, future)); 68 } 69 } 70 71 if (data.isDownloadNotes()) { 72 DownloadNotesTask task = new DownloadNotesTask(); 73 task.setZoomAfterDownload(zoom && !data.isDownloadOSMData() && !data.isDownloadGPX()); 74 Future<?> future = task.download(false, bbox, null); 75 Main.worker.submit(new PostDownloadHandler(task, future)); 76 if (zoom) { 77 tasks.add(new Pair<>(task, future)); 78 } 79 } 80 81 if (zoom && tasks.size() > 1) { 82 Main.worker.submit(() -> { 83 ProjectionBounds bounds = null; 84 // Wait for completion of download jobs 85 for (Pair<AbstractDownloadTask<?>, Future<?>> p : tasks) { 86 try { 87 p.b.get(); 88 ProjectionBounds b = p.a.getDownloadProjectionBounds(); 89 if (bounds == null) { 90 bounds = b; 91 } else if (b != null) { 92 bounds.extend(b); 93 } 94 } catch (InterruptedException | ExecutionException ex) { 95 Main.warn(ex); 96 } 97 } 98 // Zoom to the larger download bounds 99 if (Main.map != null && bounds != null) { 100 final ProjectionBounds pb = bounds; 101 GuiHelper.runInEDTAndWait(() -> Main.map.mapView.zoomTo(new ViewportData(pb))); 102 } 103 }); 104 } 105 } 106 107 @Override 108 public String getLabel() { 109 return tr("Download from OSM"); 110 } 111 112 @Override 113 public void addGui(DownloadDialog dialog) { 114 dialog.addDownloadSource(this); 115 } 116 117 @Override 118 public boolean onlyExpert() { 119 return false; 120 } 121 122 /** 123 * The GUI representation of the OSM download source. 124 */ 125 public static class OSMDownloadSourcePanel extends AbstractDownloadSourcePanel<OSMDownloadData> { 126 127 private final JCheckBox cbDownloadOsmData; 128 private final JCheckBox cbDownloadGpxData; 129 private final JCheckBox cbDownloadNotes; 130 private final JLabel sizeCheck = new JLabel(); 131 132 private static final BooleanProperty DOWNLOAD_OSM = new BooleanProperty("download.osm.data", true); 133 private static final BooleanProperty DOWNLOAD_GPS = new BooleanProperty("download.osm.gps", false); 134 private static final BooleanProperty DOWNLOAD_NOTES = new BooleanProperty("download.osm.notes", false); 135 136 public OSMDownloadSourcePanel(OSMDownloadSource ds) { 137 super(ds); 138 setLayout(new GridBagLayout()); 139 140 // size check depends on selected data source 141 final ChangeListener checkboxChangeListener = e -> 142 DownloadDialog.getInstance().getSelectedDownloadArea().ifPresent(this::updateSizeCheck); 143 144 // adding the download tasks 145 add(new JLabel(tr("Data Sources and Types:")), GBC.std().insets(5, 5, 1, 5).anchor(GBC.CENTER)); 146 cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true); 147 cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area.")); 148 cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener); 149 150 cbDownloadGpxData = new JCheckBox(tr("Raw GPS data")); 151 cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area.")); 152 cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener); 153 154 cbDownloadNotes = new JCheckBox(tr("Notes")); 155 cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area.")); 156 cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener); 157 158 Font labelFont = sizeCheck.getFont(); 159 sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize())); 160 161 add(cbDownloadOsmData, GBC.std().insets(1, 5, 1, 5)); 162 add(cbDownloadGpxData, GBC.std().insets(1, 5, 1, 5)); 163 add(cbDownloadNotes, GBC.eol().insets(1, 5, 1, 5)); 164 add(sizeCheck, GBC.eol().anchor(GBC.EAST).insets(5, 5, 5, 2)); 165 } 166 167 @Override 168 public OSMDownloadData getData() { 169 return new OSMDownloadData( 170 isDownloadOsmData(), 171 isDownloadNotes(), 172 isDownloadGpxData()); 173 } 174 175 @Override 176 public void rememberSettings() { 177 DOWNLOAD_OSM.put(isDownloadOsmData()); 178 DOWNLOAD_GPS.put(isDownloadGpxData()); 179 DOWNLOAD_NOTES.put(isDownloadNotes()); 180 } 181 182 @Override 183 public void restoreSettings() { 184 cbDownloadOsmData.setSelected(DOWNLOAD_OSM.get()); 185 cbDownloadGpxData.setSelected(DOWNLOAD_GPS.get()); 186 cbDownloadNotes.setSelected(DOWNLOAD_NOTES.get()); 187 } 188 189 @Override 190 public boolean checkDownload(Bounds bbox, DownloadSettings settings) { 191 /* 192 * It is mandatory to specify the area to download from OSM. 193 */ 194 if (bbox == null) { 195 JOptionPane.showMessageDialog( 196 this.getParent(), 197 tr("Please select a download area first."), 198 tr("Error"), 199 JOptionPane.ERROR_MESSAGE 200 ); 201 202 return false; 203 } 204 205 /* 206 * Checks if the user selected the type of data to download. At least one the following 207 * must be chosen : raw osm data, gpx data, notes. 208 * If none of those are selected, then the corresponding dialog is shown to inform the user. 209 */ 210 if (!isDownloadOsmData() && !isDownloadGpxData() && !isDownloadNotes()) { 211 JOptionPane.showMessageDialog( 212 this.getParent(), 213 tr("<html>Neither <strong>{0}</strong> nor <strong>{1}</strong> nor <strong>{2}</strong> is enabled.<br>" 214 + "Please choose to either download OSM data, or GPX data, or Notes, or all.</html>", 215 cbDownloadOsmData.getText(), 216 cbDownloadGpxData.getText(), 217 cbDownloadNotes.getText() 218 ), 219 tr("Error"), 220 JOptionPane.ERROR_MESSAGE 221 ); 222 223 return false; 224 } 225 226 this.rememberSettings(); 227 228 return true; 229 } 230 231 /** 232 * Replies true if the user selected to download OSM data 233 * 234 * @return true if the user selected to download OSM data 235 */ 236 public boolean isDownloadOsmData() { 237 return cbDownloadOsmData.isSelected(); 238 } 239 240 /** 241 * Replies true if the user selected to download GPX data 242 * 243 * @return true if the user selected to download GPX data 244 */ 245 public boolean isDownloadGpxData() { 246 return cbDownloadGpxData.isSelected(); 247 } 248 249 /** 250 * Replies true if user selected to download notes 251 * 252 * @return true if user selected to download notes 253 */ 254 public boolean isDownloadNotes() { 255 return cbDownloadNotes.isSelected(); 256 } 257 258 @Override 259 public Icon getIcon() { 260 return ImageProvider.get("download"); 261 } 262 263 @Override 264 public void boudingBoxChanged(Bounds bbox) { 265 updateSizeCheck(bbox); 266 } 267 268 private void updateSizeCheck(Bounds bbox) { 269 boolean isAreaTooLarge = false; 270 if (bbox == null) { 271 sizeCheck.setText(tr("No area selected yet")); 272 sizeCheck.setForeground(Color.darkGray); 273 } else if (!isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) { 274 isAreaTooLarge = false; 275 } else if (isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) { 276 // see max_note_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml 277 isAreaTooLarge = bbox.getArea() > Main.pref.getDouble("osm-server.max-request-area-notes", 25); 278 } else { 279 // see max_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml 280 isAreaTooLarge = bbox.getArea() > Main.pref.getDouble("osm-server.max-request-area", 0.25); 281 } 282 283 displaySizeCheckResult(isAreaTooLarge); 284 } 285 286 private void displaySizeCheckResult(boolean isAreaTooLarge) { 287 if (isAreaTooLarge) { 288 sizeCheck.setText(tr("Download area too large; will probably be rejected by server")); 289 sizeCheck.setForeground(Color.red); 290 } else { 291 sizeCheck.setText(tr("Download area ok, size probably acceptable to server")); 292 sizeCheck.setForeground(Color.darkGray); 293 } 294 } 295 296 } 297 298 /** 299 * Encapsulates data that is required to download from the OSM server. 300 */ 301 static class OSMDownloadData { 302 private boolean downloadOSMData; 303 private boolean downloadNotes; 304 private boolean downloadGPX; 305 306 OSMDownloadData(boolean downloadOSMData, boolean downloadNotes, boolean downloadGPX) { 307 this.downloadOSMData = downloadOSMData; 308 this.downloadNotes = downloadNotes; 309 this.downloadGPX = downloadGPX; 310 } 311 312 boolean isDownloadOSMData() { 313 return downloadOSMData; 314 } 315 316 boolean isDownloadNotes() { 317 return downloadNotes; 318 } 319 320 boolean isDownloadGPX() { 321 return downloadGPX; 322 } 323 } 324 } -
src/org/openstreetmap/josm/gui/download/OverpassDownloadSource.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.download; 3 4 import org.openstreetmap.josm.Main; 5 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask; 6 import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler; 7 import org.openstreetmap.josm.data.Bounds; 8 import org.openstreetmap.josm.data.preferences.BooleanProperty; 9 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 10 import org.openstreetmap.josm.gui.preferences.server.OverpassServerPreference; 11 import org.openstreetmap.josm.gui.util.GuiHelper; 12 import org.openstreetmap.josm.gui.widgets.JosmTextArea; 13 import org.openstreetmap.josm.io.OverpassDownloadReader; 14 import org.openstreetmap.josm.tools.GBC; 15 import org.openstreetmap.josm.tools.ImageProvider; 16 17 import javax.swing.AbstractAction; 18 import javax.swing.Action; 19 import javax.swing.Icon; 20 import javax.swing.JButton; 21 import javax.swing.JLabel; 22 import javax.swing.JOptionPane; 23 import javax.swing.JPanel; 24 import javax.swing.JScrollPane; 25 import javax.swing.event.ListSelectionEvent; 26 import javax.swing.event.ListSelectionListener; 27 import javax.swing.plaf.basic.BasicArrowButton; 28 import java.awt.BorderLayout; 29 import java.awt.Dimension; 30 import java.awt.GridBagLayout; 31 import java.awt.event.ActionEvent; 32 import java.awt.event.FocusEvent; 33 import java.awt.event.FocusListener; 34 import java.util.Collection; 35 import java.util.concurrent.Future; 36 import java.util.function.Consumer; 37 38 import static org.openstreetmap.josm.tools.I18n.tr; 39 40 /** 41 * Class defines the way data is fetched from Overpass API. 42 */ 43 public class OverpassDownloadSource implements DownloadSource<OverpassDownloadSource.OverpassDownloadData> { 44 45 @Override 46 public AbstractDownloadSourcePanel<OverpassDownloadData> createPanel() { 47 return new OverpassDownloadSourcePanel(this); 48 } 49 50 @Override 51 public void doDownload(Bounds bbox, OverpassDownloadData data, DownloadSettings settings) { 52 /* 53 * In order to support queries generated by the Overpass Turbo Query Wizard tool 54 * which do not require the area to be specified. 55 */ 56 Bounds area = bbox != null ? bbox : new Bounds(0, 0, 0, 0); 57 DownloadOsmTask task = new DownloadOsmTask(); 58 task.setZoomAfterDownload(settings.zoomToData()); 59 Future<?> future = task.download( 60 new OverpassDownloadReader(area, OverpassServerPreference.getOverpassServer(), data.getQuery()), 61 settings.asNewLayer(), area, null); 62 Main.worker.submit(new PostDownloadHandler(task, future, data.getErrorReporter())); 63 } 64 65 @Override 66 public String getLabel() { 67 return tr("Download from Overpass API"); 68 } 69 70 @Override 71 public void addGui(DownloadDialog dialog) { 72 dialog.addDownloadSource(this); 73 } 74 75 @Override 76 public boolean onlyExpert() { 77 return true; 78 } 79 80 /** 81 * The GUI representation of the Overpass download source. 82 */ 83 public static class OverpassDownloadSourcePanel extends AbstractDownloadSourcePanel<OverpassDownloadData> { 84 85 private JosmTextArea overpassQuery; 86 private OverpassQueryList overpassQueryList; 87 88 private static final BooleanProperty OVERPASS_QUERY_LIST_OPENED = 89 new BooleanProperty("download.overpass.query-list.opened", false); 90 private static final String ACTION_IMG_SUBDIR = "dialogs"; 91 92 public OverpassDownloadSourcePanel(OverpassDownloadSource ds) { 93 super(ds); 94 setLayout(new BorderLayout()); 95 96 String tooltip = tr("Build an Overpass query using the Overpass Turbo Query Wizard tool"); 97 Action queryWizardAction = new AbstractAction() { 98 @Override 99 public void actionPerformed(ActionEvent e) { 100 new OverpassQueryWizardDialog(OverpassDownloadSourcePanel.this).showDialog(); 101 } 102 }; 103 104 JButton openQueryWizard = new JButton(tr("Query Wizard")); 105 openQueryWizard.setToolTipText(tooltip); 106 openQueryWizard.addActionListener(queryWizardAction); 107 108 // CHECKSTYLE.OFF: LineLength 109 this.overpassQuery = new JosmTextArea( 110 "/*\n" + 111 tr("Place your Overpass query below or generate one using the Overpass Turbo Query Wizard") 112 + "\n*/", 113 8, 80); 114 // CHECKSTYLE.ON: LineLength 115 this.overpassQuery.setFont(GuiHelper.getMonospacedFont(overpassQuery)); 116 this.overpassQuery.addFocusListener(new FocusListener() { 117 @Override 118 public void focusGained(FocusEvent e) { 119 overpassQuery.selectAll(); 120 } 121 122 @Override 123 public void focusLost(FocusEvent e) { 124 // ignored 125 } 126 }); 127 128 129 this.overpassQueryList = new OverpassQueryList(this, this.overpassQuery); 130 this.overpassQueryList.setPreferredSize(new Dimension(350, 300)); 131 132 EditSnippetAction edit = new EditSnippetAction(); 133 RemoveSnippetAction remove = new RemoveSnippetAction(); 134 this.overpassQueryList.addSelectionListener(edit); 135 this.overpassQueryList.addSelectionListener(remove); 136 137 JPanel listPanel = new JPanel(new GridBagLayout()); 138 listPanel.add(new JLabel(tr("Your saved queries:")), GBC.eol().insets(2).anchor(GBC.CENTER)); 139 listPanel.add(this.overpassQueryList, GBC.eol().fill(GBC.BOTH)); 140 listPanel.add(new JButton(new AddSnippetAction()), GBC.std().fill(GBC.HORIZONTAL)); 141 listPanel.add(new JButton(edit), GBC.std().fill(GBC.HORIZONTAL)); 142 listPanel.add(new JButton(remove), GBC.std().fill(GBC.HORIZONTAL)); 143 listPanel.setVisible(OVERPASS_QUERY_LIST_OPENED.get()); 144 145 JScrollPane scrollPane = new JScrollPane(overpassQuery); 146 BasicArrowButton arrowButton = new BasicArrowButton(listPanel.isVisible() 147 ? BasicArrowButton.EAST 148 : BasicArrowButton.WEST); 149 arrowButton.setToolTipText(tr("Show/hide Overpass snippet list")); 150 arrowButton.addActionListener(e -> { 151 if (listPanel.isVisible()) { 152 listPanel.setVisible(false); 153 arrowButton.setDirection(BasicArrowButton.WEST); 154 OVERPASS_QUERY_LIST_OPENED.put(Boolean.FALSE); 155 } else { 156 listPanel.setVisible(true); 157 arrowButton.setDirection(BasicArrowButton.EAST); 158 OVERPASS_QUERY_LIST_OPENED.put(Boolean.TRUE); 159 } 160 }); 161 162 JPanel innerPanel = new JPanel(new BorderLayout()); 163 innerPanel.add(scrollPane, BorderLayout.CENTER); 164 innerPanel.add(arrowButton, BorderLayout.EAST); 165 166 JPanel leftPanel = new JPanel(new GridBagLayout()); 167 leftPanel.add(new JLabel(tr("Overpass query:")), GBC.eol().insets(5, 1, 5, 1).anchor(GBC.NORTHWEST)); 168 leftPanel.add(new JLabel(), GBC.eol().fill(GBC.VERTICAL)); 169 leftPanel.add(openQueryWizard, GBC.eol().anchor(GBC.CENTER)); 170 leftPanel.add(new JLabel(), GBC.eol().fill(GBC.VERTICAL)); 171 172 add(leftPanel, BorderLayout.WEST); 173 add(innerPanel, BorderLayout.CENTER); 174 add(listPanel, BorderLayout.EAST); 175 } 176 177 @Override 178 public OverpassDownloadData getData() { 179 String query = overpassQuery.getText(); 180 /* 181 * A callback that is passed to PostDownloadReporter that is called once the download task 182 * has finished. According to the number of errors happened, their type we decide whether we 183 * want to save the last query in OverpassQueryList. 184 */ 185 Consumer<Collection<Object>> errorReporter = errors -> { 186 187 boolean onlyNoDataError = errors.size() == 1 && 188 errors.contains("No data found in this area."); 189 190 if (errors.isEmpty() || onlyNoDataError) { 191 overpassQueryList.saveHistoricItem(query); 192 } 193 }; 194 195 return new OverpassDownloadData(query, errorReporter); 196 } 197 198 @Override 199 public void rememberSettings() { 200 // nothing 201 } 202 203 @Override 204 public void restoreSettings() { 205 // nothing 206 } 207 208 @Override 209 public boolean checkDownload(Bounds bbox, DownloadSettings settings) { 210 String query = getData().getQuery(); 211 212 /* 213 * Absence of the selected area can be justified only if the overpass query 214 * is not restricted to bbox. 215 */ 216 if (bbox == null && query.contains("{{bbox}}")) { 217 JOptionPane.showMessageDialog( 218 this.getParent(), 219 tr("Please select a download area first."), 220 tr("Error"), 221 JOptionPane.ERROR_MESSAGE 222 ); 223 return false; 224 } 225 226 /* 227 * Check for an empty query. User might want to download everything, if so validation is passed, 228 * otherwise return false. 229 */ 230 if (query.matches("(/\\*(\\*[^/]|[^\\*/])*\\*/|\\s)*")) { 231 boolean doFix = ConditionalOptionPaneUtil.showConfirmationDialog( 232 "download.overpass.fix.emptytoall", 233 this, 234 tr("You entered an empty query. Do you want to download all data in this area instead?"), 235 tr("Download all data?"), 236 JOptionPane.YES_NO_OPTION, 237 JOptionPane.QUESTION_MESSAGE, 238 JOptionPane.YES_OPTION); 239 if (doFix) { 240 String repairedQuery = "[out:xml]; \n" 241 + query + "\n" 242 + "(\n" 243 + " node({{bbox}});\n" 244 + "<;\n" 245 + ");\n" 246 + "(._;>;);" 247 + "out meta;"; 248 this.overpassQuery.setText(repairedQuery); 249 } else { 250 return false; 251 } 252 } 253 254 return true; 255 } 256 257 /** 258 * Sets query to the query text field. 259 * @param query The query to set. 260 */ 261 public void setOverpassQuery(String query) { 262 this.overpassQuery.setText(query); 263 } 264 265 @Override 266 public Icon getIcon() { 267 return ImageProvider.get("download-overpass"); 268 } 269 270 /** 271 * Action that delegates snippet creation to {@link OverpassQueryList#createNewItem()}. 272 */ 273 private class AddSnippetAction extends AbstractAction { 274 275 /** 276 * Constructs a new {@code AddSnippetAction}. 277 */ 278 AddSnippetAction() { 279 super(); 280 putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "add")); 281 putValue(SHORT_DESCRIPTION, tr("Add new snippet")); 282 } 283 284 @Override 285 public void actionPerformed(ActionEvent e) { 286 overpassQueryList.createNewItem(); 287 } 288 } 289 290 /** 291 * Action that delegates snippet removal to {@link OverpassQueryList#removeSelectedItem()}. 292 */ 293 private class RemoveSnippetAction extends AbstractAction implements ListSelectionListener { 294 295 /** 296 * Constructs a new {@code RemoveSnippetAction}. 297 */ 298 RemoveSnippetAction() { 299 super(); 300 putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "delete")); 301 putValue(SHORT_DESCRIPTION, tr("Delete selected snippet")); 302 checkEnabled(); 303 } 304 305 @Override 306 public void actionPerformed(ActionEvent e) { 307 overpassQueryList.removeSelectedItem(); 308 } 309 310 /** 311 * Disables the action if no items are selected. 312 */ 313 void checkEnabled() { 314 setEnabled(overpassQueryList.getSelectedItem().isPresent()); 315 } 316 317 @Override 318 public void valueChanged(ListSelectionEvent e) { 319 checkEnabled(); 320 } 321 } 322 323 /** 324 * Action that delegates snippet edit to {@link OverpassQueryList#editSelectedItem()}. 325 */ 326 private class EditSnippetAction extends AbstractAction implements ListSelectionListener { 327 328 /** 329 * Constructs a new {@code EditSnippetAction}. 330 */ 331 EditSnippetAction() { 332 super(); 333 putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "edit")); 334 putValue(SHORT_DESCRIPTION, tr("Edit selected snippet")); 335 checkEnabled(); 336 } 337 338 @Override 339 public void actionPerformed(ActionEvent e) { 340 overpassQueryList.editSelectedItem(); 341 } 342 343 /** 344 * Disables the action if no items are selected. 345 */ 346 void checkEnabled() { 347 setEnabled(overpassQueryList.getSelectedItem().isPresent()); 348 } 349 350 @Override 351 public void valueChanged(ListSelectionEvent e) { 352 checkEnabled(); 353 } 354 } 355 } 356 357 /** 358 * Encapsulates data that is required to preform download from Overpass API. 359 */ 360 static class OverpassDownloadData { 361 private String query; 362 private Consumer<Collection<Object>> errorReporter; 363 364 OverpassDownloadData(String query, Consumer<Collection<Object>> errorReporter) { 365 this.query = query; 366 this.errorReporter = errorReporter; 367 } 368 369 String getQuery() { 370 return this.query; 371 } 372 373 Consumer<Collection<Object>> getErrorReporter() { 374 return this.errorReporter; 375 } 376 } 377 } -
src/org/openstreetmap/josm/gui/download/OverpassQueryWizardDialog.java
19 19 import javax.swing.text.JTextComponent; 20 20 21 21 import org.openstreetmap.josm.Main; 22 import org.openstreetmap.josm.actions.OverpassDownloadAction.OverpassDownloadDialog;23 22 import org.openstreetmap.josm.data.preferences.CollectionProperty; 24 23 import org.openstreetmap.josm.gui.ExtendedDialog; 25 24 import org.openstreetmap.josm.gui.util.GuiHelper; … … 62 61 + "#desc {width: 350px;}" 63 62 + "</style>\n"; 64 63 65 private final OverpassDownload Dialog parentDialog;64 private final OverpassDownloadSource.OverpassDownloadSourcePanel dsPanel; 66 65 67 66 /** 68 67 * Create a new {@link OverpassQueryWizardDialog} 69 * @param parentDialog The parent this dialog should be displayed for68 * @param dsPanel The Overpass download source panel. 70 69 */ 71 public OverpassQueryWizardDialog(OverpassDownload Dialog parentDialog) {72 super( parentDialog, tr("Overpass Turbo Query Wizard"),70 public OverpassQueryWizardDialog(OverpassDownloadSource.OverpassDownloadSourcePanel dsPanel) { 71 super(dsPanel.getParent(), tr("Overpass Turbo Query Wizard"), 73 72 tr("Build query"), tr("Build query and execute"), tr("Cancel")); 74 this. parentDialog = parentDialog;73 this.dsPanel = dsPanel; 75 74 76 75 this.queryWizard = new HistoryComboBox(); 77 76 this.overpassQueryBuilder = OverpassTurboQueryWizard.getInstance(); … … 89 88 90 89 queryWizard.setPossibleItems(OVERPASS_WIZARD_HISTORY.get()); 91 90 92 setCancelButton(CANCEL );93 setDefaultButton(BUILD_AN_EXECUTE_QUERY + 1); // Build and execute button91 setCancelButton(CANCEL + 1); 92 setDefaultButton(BUILD_AN_EXECUTE_QUERY + 1); 94 93 setContent(panel, false); 95 94 } 96 95 … … 108 107 this.saveHistory(); 109 108 super.buttonAction(BUILD_AN_EXECUTE_QUERY, evt); 110 109 111 parentDialog.triggerDownload();110 DownloadDialog.getInstance().startDownload(); 112 111 } 113 112 break; 114 113 default: … … 141 140 } catch (UncheckedParseException ex) { 142 141 Main.error(ex); 143 142 JOptionPane.showMessageDialog( 144 parentDialog,143 dsPanel.getParent(), 145 144 "<html>" + 146 145 tr("The Overpass wizard could not parse the following query:") + 147 146 Utils.joinAsHtmlUnorderedList(Collections.singleton(searchTerm)) + … … 164 163 Optional<String> q = this.tryParseSearchTerm(wizardSearchTerm); 165 164 if (q.isPresent()) { 166 165 String query = q.get(); 167 parentDialog.setOverpassQuery(query);166 dsPanel.setOverpassQuery(query); 168 167 169 168 return true; 170 169 }