source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java@ 15587

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

see #16220 - add translation context for "Available" plugins, to distinguish from "Available" tools in toolbar

  • Property svn:eol-style set to native
File size: 26.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences.plugin;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trc;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.BorderLayout;
9import java.awt.Component;
10import java.awt.GridBagConstraints;
11import java.awt.GridBagLayout;
12import java.awt.GridLayout;
13import java.awt.Insets;
14import java.awt.event.ActionEvent;
15import java.awt.event.ComponentAdapter;
16import java.awt.event.ComponentEvent;
17import java.lang.reflect.InvocationTargetException;
18import java.util.ArrayList;
19import java.util.Arrays;
20import java.util.Collection;
21import java.util.Collections;
22import java.util.LinkedList;
23import java.util.List;
24import java.util.Set;
25import java.util.regex.Pattern;
26
27import javax.swing.AbstractAction;
28import javax.swing.BorderFactory;
29import javax.swing.ButtonGroup;
30import javax.swing.DefaultListModel;
31import javax.swing.JButton;
32import javax.swing.JCheckBox;
33import javax.swing.JLabel;
34import javax.swing.JList;
35import javax.swing.JOptionPane;
36import javax.swing.JPanel;
37import javax.swing.JRadioButton;
38import javax.swing.JScrollPane;
39import javax.swing.JTabbedPane;
40import javax.swing.JTextArea;
41import javax.swing.SwingUtilities;
42import javax.swing.UIManager;
43
44import org.openstreetmap.josm.actions.ExpertToggleAction;
45import org.openstreetmap.josm.data.Preferences;
46import org.openstreetmap.josm.data.Version;
47import org.openstreetmap.josm.gui.HelpAwareOptionPane;
48import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
49import org.openstreetmap.josm.gui.MainApplication;
50import org.openstreetmap.josm.gui.help.HelpUtil;
51import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
52import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
53import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
54import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
55import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.PreferencePanel;
56import org.openstreetmap.josm.gui.util.GuiHelper;
57import org.openstreetmap.josm.gui.widgets.FilterField;
58import org.openstreetmap.josm.plugins.PluginDownloadTask;
59import org.openstreetmap.josm.plugins.PluginHandler;
60import org.openstreetmap.josm.plugins.PluginInformation;
61import org.openstreetmap.josm.plugins.ReadLocalPluginInformationTask;
62import org.openstreetmap.josm.plugins.ReadRemotePluginInformationTask;
63import org.openstreetmap.josm.spi.preferences.Config;
64import org.openstreetmap.josm.tools.GBC;
65import org.openstreetmap.josm.tools.ImageProvider;
66import org.openstreetmap.josm.tools.Logging;
67import org.openstreetmap.josm.tools.Utils;
68
69/**
70 * Preference settings for plugins.
71 * @since 168
72 */
73public final class PluginPreference extends DefaultTabPreferenceSetting {
74
75 /**
76 * Factory used to create a new {@code PluginPreference}.
77 */
78 public static class Factory implements PreferenceSettingFactory {
79 @Override
80 public PreferenceSetting createPreferenceSetting() {
81 return new PluginPreference();
82 }
83 }
84
85 private PluginListPanel pnlPluginPreferences;
86 private PluginPreferencesModel model;
87 private JScrollPane spPluginPreferences;
88 private PluginUpdatePolicyPanel pnlPluginUpdatePolicy;
89
90 /**
91 * is set to true if this preference pane has been selected by the user
92 */
93 private boolean pluginPreferencesActivated;
94
95 private PluginPreference() {
96 super(/* ICON(preferences/) */ "plugin", tr("Plugins"), tr("Configure available plugins."), false, new JTabbedPane());
97 }
98
99 /**
100 * Returns the download summary string to be shown.
101 * @param task The plugin download task that has completed
102 * @return the download summary string to be shown. Contains summary of success/failed plugins.
103 */
104 public static String buildDownloadSummary(PluginDownloadTask task) {
105 Collection<PluginInformation> downloaded = task.getDownloadedPlugins();
106 Collection<PluginInformation> failed = task.getFailedPlugins();
107 Exception exception = task.getLastException();
108 StringBuilder sb = new StringBuilder();
109 if (!downloaded.isEmpty()) {
110 sb.append(trn(
111 "The following plugin has been downloaded <strong>successfully</strong>:",
112 "The following {0} plugins have been downloaded <strong>successfully</strong>:",
113 downloaded.size(),
114 downloaded.size()
115 ));
116 sb.append("<ul>");
117 for (PluginInformation pi: downloaded) {
118 sb.append("<li>").append(pi.name).append(" (").append(pi.version).append(")</li>");
119 }
120 sb.append("</ul>");
121 }
122 if (!failed.isEmpty()) {
123 sb.append(trn(
124 "Downloading the following plugin has <strong>failed</strong>:",
125 "Downloading the following {0} plugins has <strong>failed</strong>:",
126 failed.size(),
127 failed.size()
128 ));
129 sb.append("<ul>");
130 for (PluginInformation pi: failed) {
131 sb.append("<li>").append(pi.name).append("</li>");
132 }
133 sb.append("</ul>");
134 }
135 if (exception != null) {
136 // Same i18n string in ExceptionUtil.explainBadRequest()
137 sb.append(tr("<br>Error message(untranslated): {0}", exception.getMessage()));
138 }
139 return sb.toString();
140 }
141
142 /**
143 * Notifies user about result of a finished plugin download task.
144 * @param parent The parent component
145 * @param task The finished plugin download task
146 * @param restartRequired true if a restart is required
147 * @since 6797
148 */
149 public static void notifyDownloadResults(final Component parent, PluginDownloadTask task, boolean restartRequired) {
150 final Collection<PluginInformation> failed = task.getFailedPlugins();
151 final StringBuilder sb = new StringBuilder();
152 sb.append("<html>")
153 .append(buildDownloadSummary(task));
154 if (restartRequired) {
155 sb.append(tr("Please restart JOSM to activate the downloaded plugins."));
156 }
157 sb.append("</html>");
158 GuiHelper.runInEDTAndWait(() -> HelpAwareOptionPane.showOptionDialog(
159 parent,
160 sb.toString(),
161 tr("Update plugins"),
162 !failed.isEmpty() ? JOptionPane.WARNING_MESSAGE : JOptionPane.INFORMATION_MESSAGE,
163 HelpUtil.ht("/Preferences/Plugins")
164 ));
165 }
166
167 private JPanel buildSearchFieldPanel() {
168 JPanel pnl = new JPanel(new GridBagLayout());
169 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
170 GridBagConstraints gc = new GridBagConstraints();
171
172 gc.anchor = GridBagConstraints.NORTHWEST;
173 gc.fill = GridBagConstraints.HORIZONTAL;
174 gc.weightx = 0.0;
175 gc.insets = new Insets(0, 0, 0, 3);
176 pnl.add(GBC.glue(0, 0));
177
178 gc.weightx = 1.0;
179 ButtonGroup bg = new ButtonGroup();
180 JPanel radios = new JPanel();
181 addRadioButton(bg, radios, new JRadioButton(trc("plugins", "All"), true), gc, PluginInstallation.ALL);
182 addRadioButton(bg, radios, new JRadioButton(trc("plugins", "Installed")), gc, PluginInstallation.INSTALLED);
183 addRadioButton(bg, radios, new JRadioButton(trc("plugins", "Available")), gc, PluginInstallation.AVAILABLE);
184 pnl.add(radios, gc);
185
186 gc.gridx = 0;
187 gc.weightx = 0.0;
188 pnl.add(new JLabel(tr("Search:")), gc);
189
190 gc.gridx = 1;
191 gc.weightx = 1.0;
192 pnl.add(new FilterField().filter(expr -> {
193 model.filterDisplayedPlugins(expr);
194 pnlPluginPreferences.refreshView();
195 }), gc);
196 return pnl;
197 }
198
199 private void addRadioButton(ButtonGroup bg, JPanel pnl, JRadioButton rb, GridBagConstraints gc, PluginInstallation value) {
200 bg.add(rb);
201 pnl.add(rb, gc);
202 rb.addActionListener(e -> {
203 model.filterDisplayedPlugins(value);
204 pnlPluginPreferences.refreshView();
205 });
206 }
207
208 private static Component addButton(JPanel pnl, JButton button, String buttonName) {
209 button.setName(buttonName);
210 return pnl.add(button);
211 }
212
213 private JPanel buildActionPanel() {
214 JPanel pnl = new JPanel(new GridLayout(1, 4));
215
216 // assign some component names to these as we go to aid testing
217 addButton(pnl, new JButton(new DownloadAvailablePluginsAction()), "downloadListButton");
218 addButton(pnl, new JButton(new UpdateSelectedPluginsAction()), "updatePluginsButton");
219 ExpertToggleAction.addVisibilitySwitcher(addButton(pnl, new JButton(new SelectByListAction()), "loadFromListButton"));
220 ExpertToggleAction.addVisibilitySwitcher(addButton(pnl, new JButton(new ConfigureSitesAction()), "configureSitesButton"));
221 return pnl;
222 }
223
224 private JPanel buildPluginListPanel() {
225 JPanel pnl = new JPanel(new BorderLayout());
226 pnl.add(buildSearchFieldPanel(), BorderLayout.NORTH);
227 model = new PluginPreferencesModel();
228 pnlPluginPreferences = new PluginListPanel(model);
229 spPluginPreferences = GuiHelper.embedInVerticalScrollPane(pnlPluginPreferences);
230 spPluginPreferences.getVerticalScrollBar().addComponentListener(
231 new ComponentAdapter() {
232 @Override
233 public void componentShown(ComponentEvent e) {
234 spPluginPreferences.setBorder(UIManager.getBorder("ScrollPane.border"));
235 }
236
237 @Override
238 public void componentHidden(ComponentEvent e) {
239 spPluginPreferences.setBorder(null);
240 }
241 }
242 );
243
244 pnl.add(spPluginPreferences, BorderLayout.CENTER);
245 pnl.add(buildActionPanel(), BorderLayout.SOUTH);
246 return pnl;
247 }
248
249 private JTabbedPane buildContentPane() {
250 JTabbedPane pane = getTabPane();
251 pnlPluginUpdatePolicy = new PluginUpdatePolicyPanel();
252 pane.addTab(tr("Plugins"), buildPluginListPanel());
253 pane.addTab(tr("Plugin update policy"), pnlPluginUpdatePolicy);
254 return pane;
255 }
256
257 @Override
258 public void addGui(final PreferenceTabbedPane gui) {
259 GridBagConstraints gc = new GridBagConstraints();
260 gc.weightx = 1.0;
261 gc.weighty = 1.0;
262 gc.anchor = GridBagConstraints.NORTHWEST;
263 gc.fill = GridBagConstraints.BOTH;
264 PreferencePanel plugins = gui.createPreferenceTab(this);
265 plugins.add(buildContentPane(), gc);
266 readLocalPluginInformation();
267 pluginPreferencesActivated = true;
268 }
269
270 private void configureSites() {
271 ButtonSpec[] options = {
272 new ButtonSpec(
273 tr("OK"),
274 new ImageProvider("ok"),
275 tr("Accept the new plugin sites and close the dialog"),
276 null /* no special help topic */
277 ),
278 new ButtonSpec(
279 tr("Cancel"),
280 new ImageProvider("cancel"),
281 tr("Close the dialog"),
282 null /* no special help topic */
283 )
284 };
285 PluginConfigurationSitesPanel pnl = new PluginConfigurationSitesPanel();
286
287 int answer = HelpAwareOptionPane.showOptionDialog(
288 pnlPluginPreferences,
289 pnl,
290 tr("Configure Plugin Sites"),
291 JOptionPane.QUESTION_MESSAGE,
292 null,
293 options,
294 options[0],
295 null /* no help topic */
296 );
297 if (answer != 0 /* OK */)
298 return;
299 Preferences.main().setPluginSites(pnl.getUpdateSites());
300 }
301
302 /**
303 * Replies the set of plugins waiting for update or download
304 *
305 * @return the set of plugins waiting for update or download
306 */
307 public Set<PluginInformation> getPluginsScheduledForUpdateOrDownload() {
308 return model != null ? model.getPluginsScheduledForUpdateOrDownload() : null;
309 }
310
311 /**
312 * Replies the list of plugins which have been added by the user to the set of activated plugins
313 *
314 * @return the list of newly activated plugins
315 */
316 public List<PluginInformation> getNewlyActivatedPlugins() {
317 return model != null ? model.getNewlyActivatedPlugins() : null;
318 }
319
320 @Override
321 public boolean ok() {
322 if (!pluginPreferencesActivated)
323 return false;
324 pnlPluginUpdatePolicy.rememberInPreferences();
325 if (model.isActivePluginsChanged()) {
326 List<String> l = new LinkedList<>(model.getSelectedPluginNames());
327 Collections.sort(l);
328 Config.getPref().putList("plugins", l);
329 List<PluginInformation> deactivatedPlugins = model.getNewlyDeactivatedPlugins();
330 if (!deactivatedPlugins.isEmpty()) {
331 boolean requiresRestart = PluginHandler.removePlugins(deactivatedPlugins);
332 if (requiresRestart)
333 return requiresRestart;
334 }
335 for (PluginInformation pi : model.getNewlyActivatedPlugins()) {
336 if (!pi.canloadatruntime)
337 return true;
338 }
339 }
340 return false;
341 }
342
343 /**
344 * Reads locally available information about plugins from the local file system.
345 * Scans cached plugin lists from plugin download sites and locally available
346 * plugin jar files.
347 *
348 */
349 public void readLocalPluginInformation() {
350 final ReadLocalPluginInformationTask task = new ReadLocalPluginInformationTask();
351 Runnable r = () -> {
352 if (!task.isCanceled()) {
353 SwingUtilities.invokeLater(() -> {
354 model.setAvailablePlugins(task.getAvailablePlugins());
355 pnlPluginPreferences.refreshView();
356 });
357 }
358 };
359 MainApplication.worker.submit(task);
360 MainApplication.worker.submit(r);
361 }
362
363 /**
364 * The action for downloading the list of available plugins
365 */
366 class DownloadAvailablePluginsAction extends AbstractAction {
367
368 /**
369 * Constructs a new {@code DownloadAvailablePluginsAction}.
370 */
371 DownloadAvailablePluginsAction() {
372 putValue(NAME, tr("Download list"));
373 putValue(SHORT_DESCRIPTION, tr("Download the list of available plugins"));
374 new ImageProvider("download").getResource().attachImageIcon(this);
375 }
376
377 @Override
378 public void actionPerformed(ActionEvent e) {
379 Collection<String> pluginSites = Preferences.main().getOnlinePluginSites();
380 if (pluginSites.isEmpty()) {
381 return;
382 }
383 final ReadRemotePluginInformationTask task = new ReadRemotePluginInformationTask(pluginSites);
384 Runnable continuation = () -> {
385 if (!task.isCanceled()) {
386 SwingUtilities.invokeLater(() -> {
387 model.updateAvailablePlugins(task.getAvailablePlugins());
388 pnlPluginPreferences.refreshView();
389 Config.getPref().putInt("pluginmanager.version", Version.getInstance().getVersion()); // fix #7030
390 });
391 }
392 };
393 MainApplication.worker.submit(task);
394 MainApplication.worker.submit(continuation);
395 }
396 }
397
398 /**
399 * The action for updating the list of selected plugins
400 */
401 class UpdateSelectedPluginsAction extends AbstractAction {
402 UpdateSelectedPluginsAction() {
403 putValue(NAME, tr("Update plugins"));
404 putValue(SHORT_DESCRIPTION, tr("Update the selected plugins"));
405 new ImageProvider("dialogs", "refresh").getResource().attachImageIcon(this);
406 }
407
408 protected void alertNothingToUpdate() {
409 try {
410 SwingUtilities.invokeAndWait(() -> HelpAwareOptionPane.showOptionDialog(
411 pnlPluginPreferences,
412 tr("All installed plugins are up to date. JOSM does not have to download newer versions."),
413 tr("Plugins up to date"),
414 JOptionPane.INFORMATION_MESSAGE,
415 null // FIXME: provide help context
416 ));
417 } catch (InterruptedException | InvocationTargetException e) {
418 Logging.error(e);
419 }
420 }
421
422 @Override
423 public void actionPerformed(ActionEvent e) {
424 final List<PluginInformation> toUpdate = model.getSelectedPlugins();
425 // the async task for downloading plugins
426 final PluginDownloadTask pluginDownloadTask = new PluginDownloadTask(
427 pnlPluginPreferences,
428 toUpdate,
429 tr("Update plugins")
430 );
431 // the async task for downloading plugin information
432 final ReadRemotePluginInformationTask pluginInfoDownloadTask = new ReadRemotePluginInformationTask(
433 Preferences.main().getOnlinePluginSites());
434
435 // to be run asynchronously after the plugin download
436 //
437 final Runnable pluginDownloadContinuation = () -> {
438 if (pluginDownloadTask.isCanceled())
439 return;
440 boolean restartRequired = false;
441 for (PluginInformation pi : pluginDownloadTask.getDownloadedPlugins()) {
442 if (!model.getNewlyActivatedPlugins().contains(pi) || !pi.canloadatruntime) {
443 restartRequired = true;
444 break;
445 }
446 }
447 notifyDownloadResults(pnlPluginPreferences, pluginDownloadTask, restartRequired);
448 model.refreshLocalPluginVersion(pluginDownloadTask.getDownloadedPlugins());
449 model.clearPendingPlugins(pluginDownloadTask.getDownloadedPlugins());
450 GuiHelper.runInEDT(pnlPluginPreferences::refreshView);
451 };
452
453 // to be run asynchronously after the plugin list download
454 //
455 final Runnable pluginInfoDownloadContinuation = () -> {
456 if (pluginInfoDownloadTask.isCanceled())
457 return;
458 model.updateAvailablePlugins(pluginInfoDownloadTask.getAvailablePlugins());
459 // select plugins which actually have to be updated
460 //
461 toUpdate.removeIf(pi -> !pi.isUpdateRequired());
462 if (toUpdate.isEmpty()) {
463 alertNothingToUpdate();
464 return;
465 }
466 pluginDownloadTask.setPluginsToDownload(toUpdate);
467 MainApplication.worker.submit(pluginDownloadTask);
468 MainApplication.worker.submit(pluginDownloadContinuation);
469 };
470
471 MainApplication.worker.submit(pluginInfoDownloadTask);
472 MainApplication.worker.submit(pluginInfoDownloadContinuation);
473 }
474 }
475
476 /**
477 * The action for configuring the plugin download sites
478 *
479 */
480 class ConfigureSitesAction extends AbstractAction {
481 ConfigureSitesAction() {
482 putValue(NAME, tr("Configure sites..."));
483 putValue(SHORT_DESCRIPTION, tr("Configure the list of sites where plugins are downloaded from"));
484 new ImageProvider("dialogs", "settings").getResource().attachImageIcon(this);
485 }
486
487 @Override
488 public void actionPerformed(ActionEvent e) {
489 configureSites();
490 }
491 }
492
493 /**
494 * The action for selecting the plugins given by a text file compatible to JOSM bug report.
495 * @author Michael Zangl
496 */
497 class SelectByListAction extends AbstractAction {
498 SelectByListAction() {
499 putValue(NAME, tr("Load from list..."));
500 putValue(SHORT_DESCRIPTION, tr("Load plugins from a list of plugins"));
501 }
502
503 @Override
504 public void actionPerformed(ActionEvent e) {
505 JTextArea textField = new JTextArea(10, 0);
506 JCheckBox deleteNotInList = new JCheckBox(tr("Disable all other plugins"));
507
508 JLabel helpLabel = new JLabel("<html>" + Utils.join("<br/>", Arrays.asList(
509 tr("Enter a list of plugins you want to download."),
510 tr("You should add one plugin id per line, version information is ignored."),
511 tr("You can copy+paste the list of a status report here."))) + "</html>");
512
513 if (JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(GuiHelper.getFrameForComponent(getTabPane()),
514 new Object[] {helpLabel, new JScrollPane(textField), deleteNotInList},
515 tr("Load plugins from list"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)) {
516 activatePlugins(textField, deleteNotInList.isSelected());
517 }
518 }
519
520 private void activatePlugins(JTextArea textField, boolean deleteNotInList) {
521 String[] lines = textField.getText().split("\n");
522 List<String> toActivate = new ArrayList<>();
523 List<String> notFound = new ArrayList<>();
524 // This pattern matches the default list format JOSM uses for bug reports.
525 // It removes a list item mark at the beginning of the line: +, -, *
526 // It removes the version number after the plugin, like: 123, (123), (v5.7alpha3), (1b3), (v1-SNAPSHOT-1)...
527 Pattern regex = Pattern.compile("^[-+\\*\\s]*|\\s[\\d\\s]*(\\([^\\(\\)\\[\\]]*\\))?[\\d\\s]*$");
528 for (String line : lines) {
529 String name = regex.matcher(line).replaceAll("");
530 if (name.isEmpty()) {
531 continue;
532 }
533 PluginInformation plugin = model.getPluginInformation(name);
534 if (plugin == null) {
535 notFound.add(name);
536 } else {
537 toActivate.add(name);
538 }
539 }
540
541 if (notFound.isEmpty() || confirmIgnoreNotFound(notFound)) {
542 activatePlugins(toActivate, deleteNotInList);
543 }
544 }
545
546 private void activatePlugins(List<String> toActivate, boolean deleteNotInList) {
547 if (deleteNotInList) {
548 for (String name : model.getSelectedPluginNames()) {
549 if (!toActivate.contains(name)) {
550 model.setPluginSelected(name, false);
551 }
552 }
553 }
554 for (String name : toActivate) {
555 model.setPluginSelected(name, true);
556 }
557 pnlPluginPreferences.refreshView();
558 }
559
560 private boolean confirmIgnoreNotFound(List<String> notFound) {
561 String list = "<ul><li>" + Utils.join("</li><li>", notFound) + "</li></ul>";
562 String message = "<html>" + tr("The following plugins were not found. Continue anyway?") + list + "</html>";
563 return JOptionPane.showConfirmDialog(GuiHelper.getFrameForComponent(getTabPane()),
564 message) == JOptionPane.OK_OPTION;
565 }
566 }
567
568 private static class PluginConfigurationSitesPanel extends JPanel {
569
570 private final DefaultListModel<String> model = new DefaultListModel<>();
571
572 PluginConfigurationSitesPanel() {
573 super(new GridBagLayout());
574 add(new JLabel(tr("Add JOSM Plugin description URL.")), GBC.eol());
575 for (String s : Preferences.main().getPluginSites()) {
576 model.addElement(s);
577 }
578 final JList<String> list = new JList<>(model);
579 add(new JScrollPane(list), GBC.std().fill());
580 JPanel buttons = new JPanel(new GridBagLayout());
581 buttons.add(new JButton(new AbstractAction(tr("Add")) {
582 @Override
583 public void actionPerformed(ActionEvent e) {
584 String s = JOptionPane.showInputDialog(
585 GuiHelper.getFrameForComponent(PluginConfigurationSitesPanel.this),
586 tr("Add JOSM Plugin description URL."),
587 tr("Enter URL"),
588 JOptionPane.QUESTION_MESSAGE
589 );
590 if (s != null && !s.isEmpty()) {
591 model.addElement(s);
592 }
593 }
594 }), GBC.eol().fill(GBC.HORIZONTAL));
595 buttons.add(new JButton(new AbstractAction(tr("Edit")) {
596 @Override
597 public void actionPerformed(ActionEvent e) {
598 if (list.getSelectedValue() == null) {
599 JOptionPane.showMessageDialog(
600 GuiHelper.getFrameForComponent(PluginConfigurationSitesPanel.this),
601 tr("Please select an entry."),
602 tr("Warning"),
603 JOptionPane.WARNING_MESSAGE
604 );
605 return;
606 }
607 String s = (String) JOptionPane.showInputDialog(
608 MainApplication.getMainFrame(),
609 tr("Edit JOSM Plugin description URL."),
610 tr("JOSM Plugin description URL"),
611 JOptionPane.QUESTION_MESSAGE,
612 null,
613 null,
614 list.getSelectedValue()
615 );
616 if (s != null && !s.isEmpty()) {
617 model.setElementAt(s, list.getSelectedIndex());
618 }
619 }
620 }), GBC.eol().fill(GBC.HORIZONTAL));
621 buttons.add(new JButton(new AbstractAction(tr("Delete")) {
622 @Override
623 public void actionPerformed(ActionEvent event) {
624 if (list.getSelectedValue() == null) {
625 JOptionPane.showMessageDialog(
626 GuiHelper.getFrameForComponent(PluginConfigurationSitesPanel.this),
627 tr("Please select an entry."),
628 tr("Warning"),
629 JOptionPane.WARNING_MESSAGE
630 );
631 return;
632 }
633 model.removeElement(list.getSelectedValue());
634 }
635 }), GBC.eol().fill(GBC.HORIZONTAL));
636 add(buttons, GBC.eol());
637 }
638
639 protected List<String> getUpdateSites() {
640 if (model.getSize() == 0)
641 return Collections.emptyList();
642 List<String> ret = new ArrayList<>(model.getSize());
643 for (int i = 0; i < model.getSize(); i++) {
644 ret.add(model.get(i));
645 }
646 return ret;
647 }
648 }
649
650 @Override
651 public String getHelpContext() {
652 return HelpUtil.ht("/Preferences/Plugins");
653 }
654}
Note: See TracBrowser for help on using the repository browser.