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

Last change on this file since 12841 was 12841, checked in by bastiK, 6 weeks ago

see #15229 - fix deprecations caused by [12840]

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