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

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

fix #16220 - filter plugins by installation state

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