001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions; 003 004import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 005import static org.openstreetmap.josm.tools.I18n.tr; 006 007import java.awt.GridBagConstraints; 008import java.awt.GridBagLayout; 009import java.awt.GridLayout; 010import java.awt.event.ActionEvent; 011import java.awt.event.KeyEvent; 012import java.util.ArrayList; 013import java.util.Collection; 014import java.util.Collections; 015import java.util.LinkedList; 016import java.util.List; 017import java.util.concurrent.Future; 018 019import javax.swing.JCheckBox; 020import javax.swing.JLabel; 021import javax.swing.JList; 022import javax.swing.JOptionPane; 023import javax.swing.JPanel; 024 025import org.openstreetmap.josm.Main; 026import org.openstreetmap.josm.actions.downloadtasks.DownloadGeoUrlTask; 027import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask; 028import org.openstreetmap.josm.actions.downloadtasks.DownloadNotesTask; 029import org.openstreetmap.josm.actions.downloadtasks.DownloadNotesUrlBoundsTask; 030import org.openstreetmap.josm.actions.downloadtasks.DownloadNotesUrlIdTask; 031import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmChangeCompressedTask; 032import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmChangeTask; 033import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmCompressedTask; 034import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmIdTask; 035import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask; 036import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmUrlTask; 037import org.openstreetmap.josm.actions.downloadtasks.DownloadSessionTask; 038import org.openstreetmap.josm.actions.downloadtasks.DownloadTask; 039import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler; 040import org.openstreetmap.josm.gui.ExtendedDialog; 041import org.openstreetmap.josm.gui.HelpAwareOptionPane; 042import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor; 043import org.openstreetmap.josm.gui.widgets.HistoryComboBox; 044import org.openstreetmap.josm.tools.Shortcut; 045import org.openstreetmap.josm.tools.Utils; 046 047/** 048 * Open an URL input dialog and load data from the given URL. 049 * 050 * @author imi 051 */ 052public class OpenLocationAction extends JosmAction { 053 054 protected final transient List<Class<? extends DownloadTask>> downloadTasks; 055 056 /** 057 * Create an open action. The name is "Open a file". 058 */ 059 public OpenLocationAction() { 060 /* I18N: Command to download a specific location/URL */ 061 super(tr("Open Location..."), "openlocation", tr("Open an URL."), 062 Shortcut.registerShortcut("system:open_location", tr("File: {0}", tr("Open Location...")), 063 KeyEvent.VK_L, Shortcut.CTRL), true); 064 putValue("help", ht("/Action/OpenLocation")); 065 this.downloadTasks = new ArrayList<>(); 066 addDownloadTaskClass(DownloadOsmTask.class); 067 addDownloadTaskClass(DownloadGpsTask.class); 068 addDownloadTaskClass(DownloadNotesTask.class); 069 addDownloadTaskClass(DownloadOsmChangeTask.class); 070 addDownloadTaskClass(DownloadOsmUrlTask.class); 071 addDownloadTaskClass(DownloadGeoUrlTask.class); 072 addDownloadTaskClass(DownloadOsmIdTask.class); 073 addDownloadTaskClass(DownloadOsmCompressedTask.class); 074 addDownloadTaskClass(DownloadOsmChangeCompressedTask.class); 075 addDownloadTaskClass(DownloadSessionTask.class); 076 addDownloadTaskClass(DownloadNotesUrlBoundsTask.class); 077 addDownloadTaskClass(DownloadNotesUrlIdTask.class); 078 } 079 080 /** 081 * Restore the current history from the preferences 082 * 083 * @param cbHistory the history combo box 084 */ 085 protected void restoreUploadAddressHistory(HistoryComboBox cbHistory) { 086 List<String> cmtHistory = new LinkedList<>(Main.pref.getCollection(getClass().getName() + ".uploadAddressHistory", 087 new LinkedList<String>())); 088 // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement() 089 // 090 Collections.reverse(cmtHistory); 091 cbHistory.setPossibleItems(cmtHistory); 092 } 093 094 /** 095 * Remind the current history in the preferences 096 * @param cbHistory the history combo box 097 */ 098 protected void remindUploadAddressHistory(HistoryComboBox cbHistory) { 099 cbHistory.addCurrentItemToHistory(); 100 Main.pref.putCollection(getClass().getName() + ".uploadAddressHistory", cbHistory.getHistory()); 101 } 102 103 @Override 104 public void actionPerformed(ActionEvent e) { 105 106 JCheckBox layer = new JCheckBox(tr("Separate Layer")); 107 layer.setToolTipText(tr("Select if the data should be downloaded into a new layer")); 108 layer.setSelected(Main.pref.getBoolean("download.newlayer")); 109 JPanel all = new JPanel(new GridBagLayout()); 110 GridBagConstraints gc = new GridBagConstraints(); 111 gc.fill = GridBagConstraints.HORIZONTAL; 112 gc.weightx = 1.0; 113 gc.anchor = GridBagConstraints.FIRST_LINE_START; 114 all.add(new JLabel(tr("Enter URL to download:")), gc); 115 HistoryComboBox uploadAddresses = new HistoryComboBox(); 116 uploadAddresses.setToolTipText(tr("Enter an URL from where data should be downloaded")); 117 restoreUploadAddressHistory(uploadAddresses); 118 gc.gridy = 1; 119 all.add(uploadAddresses, gc); 120 gc.gridy = 2; 121 gc.fill = GridBagConstraints.BOTH; 122 gc.weighty = 1.0; 123 all.add(layer, gc); 124 ExtendedDialog dialog = new ExtendedDialog(Main.parent, 125 tr("Download Location"), 126 new String[] {tr("Download URL"), tr("Cancel")} 127 ); 128 dialog.setContent(all, false /* don't embedded content in JScrollpane */); 129 dialog.setButtonIcons(new String[] {"download", "cancel"}); 130 dialog.setToolTipTexts(new String[] { 131 tr("Start downloading data"), 132 tr("Close dialog and cancel downloading") 133 }); 134 dialog.configureContextsensitiveHelp("/Action/OpenLocation", true /* show help button */); 135 dialog.showDialog(); 136 if (dialog.getValue() != 1) return; 137 remindUploadAddressHistory(uploadAddresses); 138 openUrl(layer.isSelected(), Utils.strip(uploadAddresses.getText())); 139 } 140 141 /** 142 * Replies the list of download tasks accepting the given url. 143 * @param url The URL to open 144 * @param isRemotecontrol True if download request comes from remotecontrol. 145 * @return The list of download tasks accepting the given url. 146 * @since 5691 147 */ 148 public Collection<DownloadTask> findDownloadTasks(final String url, boolean isRemotecontrol) { 149 List<DownloadTask> result = new ArrayList<>(); 150 for (Class<? extends DownloadTask> taskClass : downloadTasks) { 151 if (taskClass != null) { 152 try { 153 DownloadTask task = taskClass.getConstructor().newInstance(); 154 if (task.acceptsUrl(url, isRemotecontrol)) { 155 result.add(task); 156 } 157 } catch (ReflectiveOperationException e) { 158 Main.error(e); 159 } 160 } 161 } 162 return result; 163 } 164 165 /** 166 * Summarizes acceptable urls for error message purposes. 167 * @return The HTML message to be displayed 168 * @since 6031 169 */ 170 public String findSummaryDocumentation() { 171 StringBuilder result = new StringBuilder("<table>"); 172 for (Class<? extends DownloadTask> taskClass : downloadTasks) { 173 if (taskClass != null) { 174 try { 175 DownloadTask task = taskClass.getConstructor().newInstance(); 176 result.append(task.acceptsDocumentationSummary()); 177 } catch (ReflectiveOperationException e) { 178 Main.error(e); 179 } 180 } 181 } 182 result.append("</table>"); 183 return result.toString(); 184 } 185 186 /** 187 * Open the given URL. 188 * @param newLayer true if the URL needs to be opened in a new layer, false otherwise 189 * @param url The URL to open 190 */ 191 public void openUrl(boolean newLayer, final String url) { 192 Collection<DownloadTask> tasks = findDownloadTasks(url, false); 193 194 if (tasks.size() > 1) { 195 tasks = askWhichTasksToLoad(tasks); 196 } else if (tasks.isEmpty()) { 197 warnNoSuitableTasks(url); 198 return; 199 } 200 201 PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor(tr("Download Data")); 202 203 for (final DownloadTask task : tasks) { 204 try { 205 Future<?> future = task.loadUrl(newLayer, url, monitor); 206 Main.worker.submit(new PostDownloadHandler(task, future)); 207 } catch (IllegalArgumentException e) { 208 Main.error(e); 209 } 210 } 211 } 212 213 /** 214 * Asks the user which of the possible tasks to perform. 215 * @param tasks a list of possible tasks 216 * @return the selected tasks from the user or an empty list if the dialog has been canceled 217 */ 218 Collection<DownloadTask> askWhichTasksToLoad(final Collection<DownloadTask> tasks) { 219 final JList<DownloadTask> list = new JList<>(tasks.toArray(new DownloadTask[tasks.size()])); 220 list.addSelectionInterval(0, tasks.size() - 1); 221 final ExtendedDialog dialog = new ExtendedDialog(Main.parent, 222 tr("Which tasks to perform?"), new String[]{tr("Ok"), tr("Cancel")}, true) { { 223 setButtonIcons(new String[]{"ok", "cancel"}); 224 final JPanel pane = new JPanel(new GridLayout(2, 1)); 225 pane.add(new JLabel(tr("Which tasks to perform?"))); 226 pane.add(list); 227 setContent(pane); 228 } }; 229 dialog.showDialog(); 230 return dialog.getValue() == 1 ? list.getSelectedValuesList() : Collections.<DownloadTask>emptyList(); 231 } 232 233 /** 234 * Displays an error message dialog that no suitable tasks have been found for the given url. 235 * @param url the given url 236 */ 237 void warnNoSuitableTasks(final String url) { 238 final String details = findSummaryDocumentation(); // Explain what patterns are supported 239 HelpAwareOptionPane.showMessageDialogInEDT(Main.parent, "<html><p>" + tr( 240 "Cannot open URL ''{0}''<br>The following download tasks accept the URL patterns shown:<br>{1}", 241 url, details) + "</p></html>", tr("Download Location"), JOptionPane.ERROR_MESSAGE, ht("/Action/OpenLocation")); 242 } 243 244 /** 245 * Adds a new download task to the supported ones. 246 * @param taskClass The new download task to add 247 * @return <tt>true</tt> (as specified by {@link Collection#add}) 248 */ 249 public final boolean addDownloadTaskClass(Class<? extends DownloadTask> taskClass) { 250 return this.downloadTasks.add(taskClass); 251 } 252}