[8684] | 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 |
|
---|
[8756] | 7 | import java.awt.BorderLayout;
|
---|
[8684] | 8 | import java.awt.Component;
|
---|
[8756] | 9 | import java.awt.GridLayout;
|
---|
| 10 | import java.awt.Rectangle;
|
---|
[8684] | 11 | import java.awt.event.ActionEvent;
|
---|
[8756] | 12 | import java.awt.event.ActionListener;
|
---|
[8932] | 13 | import java.awt.event.FocusEvent;
|
---|
| 14 | import java.awt.event.FocusListener;
|
---|
[8712] | 15 | import java.awt.event.KeyEvent;
|
---|
[8684] | 16 | import java.util.ArrayList;
|
---|
[8756] | 17 | import java.util.Collection;
|
---|
[8684] | 18 | import java.util.Collections;
|
---|
[8756] | 19 | import java.util.Deque;
|
---|
| 20 | import java.util.LinkedList;
|
---|
[8684] | 21 | import java.util.concurrent.Future;
|
---|
| 22 |
|
---|
[8685] | 23 | import javax.swing.AbstractAction;
|
---|
[8932] | 24 | import javax.swing.Action;
|
---|
| 25 | import javax.swing.ActionMap;
|
---|
[8685] | 26 | import javax.swing.JButton;
|
---|
[8756] | 27 | import javax.swing.JComponent;
|
---|
[8685] | 28 | import javax.swing.JLabel;
|
---|
[8756] | 29 | import javax.swing.JMenuItem;
|
---|
[8685] | 30 | import javax.swing.JOptionPane;
|
---|
| 31 | import javax.swing.JPanel;
|
---|
[8756] | 32 | import javax.swing.JPopupMenu;
|
---|
[8685] | 33 | import javax.swing.JScrollPane;
|
---|
[8756] | 34 | import javax.swing.plaf.basic.BasicArrowButton;
|
---|
[8684] | 35 |
|
---|
| 36 | import org.openstreetmap.josm.Main;
|
---|
| 37 | import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
|
---|
| 38 | import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
|
---|
| 39 | import org.openstreetmap.josm.data.Bounds;
|
---|
| 40 | import org.openstreetmap.josm.data.preferences.CollectionProperty;
|
---|
[8756] | 41 | import org.openstreetmap.josm.data.preferences.IntegerProperty;
|
---|
[8684] | 42 | import org.openstreetmap.josm.gui.HelpAwareOptionPane;
|
---|
| 43 | import org.openstreetmap.josm.gui.download.DownloadDialog;
|
---|
[9242] | 44 | import org.openstreetmap.josm.gui.preferences.server.OverpassServerPreference;
|
---|
[8711] | 45 | import org.openstreetmap.josm.gui.util.GuiHelper;
|
---|
[8684] | 46 | import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
|
---|
[8716] | 47 | import org.openstreetmap.josm.gui.widgets.JosmTextArea;
|
---|
[8744] | 48 | import org.openstreetmap.josm.io.OverpassDownloadReader;
|
---|
[8684] | 49 | import org.openstreetmap.josm.tools.GBC;
|
---|
[10782] | 50 | import org.openstreetmap.josm.tools.InputMapUtils;
|
---|
[8744] | 51 | import org.openstreetmap.josm.tools.OverpassTurboQueryWizard;
|
---|
[8712] | 52 | import org.openstreetmap.josm.tools.Shortcut;
|
---|
[9385] | 53 | import org.openstreetmap.josm.tools.UncheckedParseException;
|
---|
[8684] | 54 | import org.openstreetmap.josm.tools.Utils;
|
---|
| 55 |
|
---|
[8685] | 56 | /**
|
---|
| 57 | * Download map data from Overpass API server.
|
---|
| 58 | * @since 8684
|
---|
| 59 | */
|
---|
[8684] | 60 | public class OverpassDownloadAction extends JosmAction {
|
---|
| 61 |
|
---|
[8685] | 62 | /**
|
---|
| 63 | * Constructs a new {@code OverpassDownloadAction}.
|
---|
| 64 | */
|
---|
[8684] | 65 | public OverpassDownloadAction() {
|
---|
| 66 | super(tr("Download from Overpass API ..."), "download-overpass", tr("Download map data from Overpass API server."),
|
---|
[8727] | 67 | // CHECKSTYLE.OFF: LineLength
|
---|
[8712] | 68 | Shortcut.registerShortcut("file:download-overpass", tr("File: {0}", tr("Download from Overpass API ...")), KeyEvent.VK_DOWN, Shortcut.ALT_SHIFT),
|
---|
[8727] | 69 | // CHECKSTYLE.ON: LineLength
|
---|
[8712] | 70 | true, "overpassdownload/download", true);
|
---|
[8684] | 71 | putValue("help", ht("/Action/OverpassDownload"));
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 | @Override
|
---|
| 75 | public void actionPerformed(ActionEvent e) {
|
---|
| 76 | OverpassDownloadDialog dialog = OverpassDownloadDialog.getInstance();
|
---|
| 77 | dialog.restoreSettings();
|
---|
| 78 | dialog.setVisible(true);
|
---|
| 79 | if (!dialog.isCanceled()) {
|
---|
| 80 | dialog.rememberSettings();
|
---|
| 81 | Bounds area = dialog.getSelectedDownloadArea();
|
---|
| 82 | DownloadOsmTask task = new DownloadOsmTask();
|
---|
[11658] | 83 | task.setZoomAfterDownload(dialog.isZoomToDownloadedDataRequired());
|
---|
[8684] | 84 | Future<?> future = task.download(
|
---|
[9242] | 85 | new OverpassDownloadReader(area, OverpassServerPreference.getOverpassServer(), dialog.getOverpassQuery()),
|
---|
[8684] | 86 | dialog.isNewLayerRequired(), area, null);
|
---|
| 87 | Main.worker.submit(new PostDownloadHandler(task, future));
|
---|
| 88 | }
|
---|
| 89 | }
|
---|
| 90 |
|
---|
[8932] | 91 | private static final class DisableActionsFocusListener implements FocusListener {
|
---|
[8684] | 92 |
|
---|
[8932] | 93 | private final ActionMap actionMap;
|
---|
| 94 |
|
---|
| 95 | private DisableActionsFocusListener(ActionMap actionMap) {
|
---|
| 96 | this.actionMap = actionMap;
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | @Override
|
---|
| 100 | public void focusGained(FocusEvent e) {
|
---|
| 101 | enableActions(false);
|
---|
| 102 | }
|
---|
| 103 |
|
---|
| 104 | @Override
|
---|
| 105 | public void focusLost(FocusEvent e) {
|
---|
| 106 | enableActions(true);
|
---|
| 107 | }
|
---|
| 108 |
|
---|
| 109 | private void enableActions(boolean enabled) {
|
---|
[10131] | 110 | Object[] allKeys = actionMap.allKeys();
|
---|
| 111 | if (allKeys != null) {
|
---|
| 112 | for (Object key : allKeys) {
|
---|
| 113 | Action action = actionMap.get(key);
|
---|
| 114 | if (action != null) {
|
---|
| 115 | action.setEnabled(enabled);
|
---|
| 116 | }
|
---|
[8932] | 117 | }
|
---|
| 118 | }
|
---|
| 119 | }
|
---|
| 120 | }
|
---|
| 121 |
|
---|
| 122 | private static final class OverpassDownloadDialog extends DownloadDialog {
|
---|
| 123 |
|
---|
[10252] | 124 | private HistoryComboBox overpassWizard;
|
---|
| 125 | private JosmTextArea overpassQuery;
|
---|
[8684] | 126 | private static OverpassDownloadDialog instance;
|
---|
[8932] | 127 | private static final CollectionProperty OVERPASS_WIZARD_HISTORY = new CollectionProperty("download.overpass.wizard",
|
---|
| 128 | new ArrayList<String>());
|
---|
[8684] | 129 |
|
---|
| 130 | private OverpassDownloadDialog(Component parent) {
|
---|
[8713] | 131 | super(parent, ht("/Action/OverpassDownload"));
|
---|
[8684] | 132 | cbDownloadOsmData.setEnabled(false);
|
---|
| 133 | cbDownloadOsmData.setSelected(false);
|
---|
| 134 | cbDownloadGpxData.setVisible(false);
|
---|
| 135 | cbDownloadNotes.setVisible(false);
|
---|
| 136 | cbStartup.setVisible(false);
|
---|
| 137 | }
|
---|
| 138 |
|
---|
[8685] | 139 | public static OverpassDownloadDialog getInstance() {
|
---|
[8684] | 140 | if (instance == null) {
|
---|
| 141 | instance = new OverpassDownloadDialog(Main.parent);
|
---|
| 142 | }
|
---|
| 143 | return instance;
|
---|
| 144 | }
|
---|
| 145 |
|
---|
| 146 | @Override
|
---|
| 147 | protected void buildMainPanelAboveDownloadSelections(JPanel pnl) {
|
---|
| 148 |
|
---|
[8932] | 149 | DisableActionsFocusListener disableActionsFocusListener =
|
---|
| 150 | new DisableActionsFocusListener(slippyMapChooser.getNavigationComponentActionMap());
|
---|
| 151 |
|
---|
[8684] | 152 | pnl.add(new JLabel(), GBC.eol()); // needed for the invisible checkboxes cbDownloadGpxData, cbDownloadNotes
|
---|
| 153 |
|
---|
| 154 | final String tooltip = tr("Builds an Overpass query using the Overpass Turbo query wizard");
|
---|
[10252] | 155 | overpassWizard = new HistoryComboBox();
|
---|
[8684] | 156 | overpassWizard.setToolTipText(tooltip);
|
---|
[9484] | 157 | overpassWizard.getEditorComponent().addFocusListener(disableActionsFocusListener);
|
---|
[8684] | 158 | final JButton buildQuery = new JButton(tr("Build query"));
|
---|
[10782] | 159 | final Action buildQueryAction = new AbstractAction() {
|
---|
[8684] | 160 | @Override
|
---|
| 161 | public void actionPerformed(ActionEvent e) {
|
---|
| 162 | final String overpassWizardText = overpassWizard.getText();
|
---|
| 163 | try {
|
---|
| 164 | overpassQuery.setText(OverpassTurboQueryWizard.getInstance().constructQuery(overpassWizardText));
|
---|
[9385] | 165 | } catch (UncheckedParseException ex) {
|
---|
[10101] | 166 | Main.error(ex);
|
---|
[8684] | 167 | HelpAwareOptionPane.showOptionDialog(
|
---|
| 168 | Main.parent,
|
---|
| 169 | tr("<html>The Overpass wizard could not parse the following query:"
|
---|
| 170 | + Utils.joinAsHtmlUnorderedList(Collections.singleton(overpassWizardText))),
|
---|
| 171 | tr("Parse error"),
|
---|
| 172 | JOptionPane.ERROR_MESSAGE,
|
---|
| 173 | null
|
---|
| 174 | );
|
---|
| 175 | }
|
---|
| 176 | }
|
---|
[10782] | 177 | };
|
---|
| 178 | buildQuery.addActionListener(buildQueryAction);
|
---|
[8684] | 179 | buildQuery.setToolTipText(tooltip);
|
---|
| 180 | pnl.add(buildQuery, GBC.std().insets(5, 5, 5, 5));
|
---|
| 181 | pnl.add(overpassWizard, GBC.eol().fill(GBC.HORIZONTAL));
|
---|
[10782] | 182 | InputMapUtils.addEnterAction(overpassWizard.getEditorComponent(), buildQueryAction);
|
---|
[8684] | 183 |
|
---|
[10252] | 184 | overpassQuery = new JosmTextArea("", 8, 80);
|
---|
[8711] | 185 | overpassQuery.setFont(GuiHelper.getMonospacedFont(overpassQuery));
|
---|
[8932] | 186 | overpassQuery.addFocusListener(disableActionsFocusListener);
|
---|
[8684] | 187 | JScrollPane scrollPane = new JScrollPane(overpassQuery);
|
---|
[8756] | 188 | final JPanel pane = new JPanel(new BorderLayout());
|
---|
| 189 | final BasicArrowButton arrowButton = new BasicArrowButton(BasicArrowButton.SOUTH);
|
---|
| 190 | arrowButton.addActionListener(new AbstractAction() {
|
---|
| 191 | @Override
|
---|
| 192 | public void actionPerformed(ActionEvent e) {
|
---|
| 193 | OverpassQueryHistoryPopup.show(arrowButton, OverpassDownloadDialog.this);
|
---|
| 194 | }
|
---|
| 195 | });
|
---|
| 196 | pane.add(scrollPane, BorderLayout.CENTER);
|
---|
| 197 | pane.add(arrowButton, BorderLayout.EAST);
|
---|
[8684] | 198 | pnl.add(new JLabel(tr("Overpass query: ")), GBC.std().insets(5, 5, 5, 5));
|
---|
| 199 | GBC gbc = GBC.eol().fill(GBC.HORIZONTAL);
|
---|
| 200 | gbc.ipady = 200;
|
---|
[8756] | 201 | pnl.add(pane, gbc);
|
---|
[8688] | 202 |
|
---|
[8684] | 203 | }
|
---|
| 204 |
|
---|
| 205 | public String getOverpassQuery() {
|
---|
| 206 | return overpassQuery.getText();
|
---|
| 207 | }
|
---|
| 208 |
|
---|
[8756] | 209 | public void setOverpassQuery(String text) {
|
---|
| 210 | overpassQuery.setText(text);
|
---|
| 211 | }
|
---|
| 212 |
|
---|
[8684] | 213 | @Override
|
---|
| 214 | public void restoreSettings() {
|
---|
| 215 | super.restoreSettings();
|
---|
| 216 | overpassWizard.setPossibleItems(OVERPASS_WIZARD_HISTORY.get());
|
---|
| 217 | }
|
---|
| 218 |
|
---|
| 219 | @Override
|
---|
| 220 | public void rememberSettings() {
|
---|
| 221 | super.rememberSettings();
|
---|
| 222 | overpassWizard.addCurrentItemToHistory();
|
---|
| 223 | OVERPASS_WIZARD_HISTORY.put(overpassWizard.getHistory());
|
---|
[8756] | 224 | OverpassQueryHistoryPopup.addToHistory(getOverpassQuery());
|
---|
[8684] | 225 | }
|
---|
| 226 |
|
---|
[10629] | 227 | @Override
|
---|
| 228 | protected void updateSizeCheck() {
|
---|
| 229 | displaySizeCheckResult(false);
|
---|
| 230 | }
|
---|
[8684] | 231 | }
|
---|
| 232 |
|
---|
[8756] | 233 | static class OverpassQueryHistoryPopup extends JPopupMenu {
|
---|
| 234 |
|
---|
| 235 | static final CollectionProperty OVERPASS_QUERY_HISTORY = new CollectionProperty("download.overpass.query", new ArrayList<String>());
|
---|
| 236 | static final IntegerProperty OVERPASS_QUERY_HISTORY_SIZE = new IntegerProperty("download.overpass.query.size", 12);
|
---|
| 237 |
|
---|
| 238 | OverpassQueryHistoryPopup(final OverpassDownloadDialog dialog) {
|
---|
| 239 | final Collection<String> history = OVERPASS_QUERY_HISTORY.get();
|
---|
| 240 | setLayout(new GridLayout((int) Math.ceil(history.size() / 2.), 2));
|
---|
| 241 | for (final String i : history) {
|
---|
| 242 | add(new OverpassQueryHistoryItem(i, dialog));
|
---|
| 243 | }
|
---|
| 244 | }
|
---|
| 245 |
|
---|
| 246 | static void show(final JComponent parent, final OverpassDownloadDialog dialog) {
|
---|
| 247 | final OverpassQueryHistoryPopup menu = new OverpassQueryHistoryPopup(dialog);
|
---|
| 248 | final Rectangle r = parent.getBounds();
|
---|
| 249 | menu.show(parent.getParent(), r.x + r.width - (int) menu.getPreferredSize().getWidth(), r.y + r.height);
|
---|
| 250 | }
|
---|
| 251 |
|
---|
| 252 | static void addToHistory(final String query) {
|
---|
| 253 | final Deque<String> history = new LinkedList<>(OVERPASS_QUERY_HISTORY.get());
|
---|
| 254 | if (!history.contains(query)) {
|
---|
| 255 | history.add(query);
|
---|
| 256 | }
|
---|
| 257 | while (history.size() > OVERPASS_QUERY_HISTORY_SIZE.get()) {
|
---|
| 258 | history.removeFirst();
|
---|
| 259 | }
|
---|
| 260 | OVERPASS_QUERY_HISTORY.put(history);
|
---|
| 261 | }
|
---|
| 262 | }
|
---|
| 263 |
|
---|
| 264 | static class OverpassQueryHistoryItem extends JMenuItem implements ActionListener {
|
---|
| 265 |
|
---|
| 266 | final String query;
|
---|
| 267 | final OverpassDownloadDialog dialog;
|
---|
| 268 |
|
---|
| 269 | OverpassQueryHistoryItem(final String query, final OverpassDownloadDialog dialog) {
|
---|
| 270 | this.query = query;
|
---|
| 271 | this.dialog = dialog;
|
---|
| 272 | setText("<html><pre style='width:300px;'>" +
|
---|
| 273 | Utils.escapeReservedCharactersHTML(Utils.restrictStringLines(query, 7)));
|
---|
| 274 | addActionListener(this);
|
---|
| 275 | }
|
---|
| 276 |
|
---|
| 277 | @Override
|
---|
| 278 | public void actionPerformed(ActionEvent e) {
|
---|
| 279 | dialog.setOverpassQuery(query);
|
---|
| 280 | }
|
---|
| 281 | }
|
---|
| 282 |
|
---|
[8684] | 283 | }
|
---|