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

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

see #16010 - use JMockit to enable more extensive test coverage (patch by ris, modified)

see https://github.com/openstreetmap/josm/pull/24/commits for details

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