001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.preferences.plugin;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.GridBagConstraints;
007import java.awt.GridBagLayout;
008import java.awt.Insets;
009import java.awt.Rectangle;
010import java.awt.event.MouseAdapter;
011import java.awt.event.MouseEvent;
012import java.util.List;
013
014import javax.swing.JLabel;
015import javax.swing.SwingConstants;
016import javax.swing.SwingUtilities;
017
018import org.openstreetmap.josm.gui.widgets.HtmlPanel;
019import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
020import org.openstreetmap.josm.plugins.PluginInformation;
021
022/**
023 * A panel displaying the list of known plugins.
024 */
025public class PluginListPanel extends VerticallyScrollablePanel {
026    static final class PluginCheckBoxMouseAdapter extends MouseAdapter {
027        private final PluginCheckBox cbPlugin;
028
029        PluginCheckBoxMouseAdapter(PluginCheckBox cbPlugin) {
030            this.cbPlugin = cbPlugin;
031        }
032
033        @Override
034        public void mouseClicked(MouseEvent e) {
035            cbPlugin.doClick();
036        }
037    }
038
039    private transient PluginPreferencesModel model;
040
041    /**
042     * Constructs a new {@code PluginListPanel} with a default model.
043     */
044    public PluginListPanel() {
045        this(new PluginPreferencesModel());
046    }
047
048    /**
049     * Constructs a new {@code PluginListPanel} with a given model.
050     * @param model The plugin model
051     */
052    public PluginListPanel(PluginPreferencesModel model) {
053        this.model = model;
054        setLayout(new GridBagLayout());
055    }
056
057    protected static String formatPluginRemoteVersion(PluginInformation pi) {
058        StringBuilder sb = new StringBuilder();
059        if (pi.version == null || pi.version.trim().isEmpty()) {
060            sb.append(tr("unknown"));
061        } else {
062            sb.append(pi.version);
063            if (pi.oldmode) {
064                sb.append('*');
065            }
066        }
067        return sb.toString();
068    }
069
070    protected static String formatPluginLocalVersion(PluginInformation pi) {
071        if (pi == null)
072            return tr("unknown");
073        if (pi.localversion == null || pi.localversion.trim().isEmpty())
074            return tr("unknown");
075        return pi.localversion;
076    }
077
078    protected static String formatCheckboxTooltipText(PluginInformation pi) {
079        if (pi == null)
080            return "";
081        if (pi.downloadlink == null)
082            return tr("Plugin bundled with JOSM");
083        else
084            return pi.downloadlink;
085    }
086
087    /**
088     * Displays a message when the plugin list is empty.
089     */
090    public void displayEmptyPluginListInformation() {
091        GridBagConstraints gbc = new GridBagConstraints();
092        gbc.gridx = 0;
093        gbc.anchor = GridBagConstraints.CENTER;
094        gbc.fill = GridBagConstraints.BOTH;
095        gbc.insets = new Insets(40, 0, 40, 0);
096        gbc.weightx = 1.0;
097        gbc.weighty = 1.0;
098
099        HtmlPanel hint = new HtmlPanel();
100        hint.setText(
101                "<html>"
102                + (model.getAvailablePlugins().isEmpty() ?
103                        tr("Please click on <strong>Download list</strong> to download and display a list of available plugins.") :
104                        tr("The filter returned no results."))
105                + "</html>"
106        );
107        add(hint, gbc);
108    }
109
110    /**
111     * Displays a list of plugins.
112     * @param displayedPlugins list of plugins
113     * @since 13799
114     */
115    public void displayPluginList(List<PluginInformation> displayedPlugins) {
116        GridBagConstraints gbc = new GridBagConstraints();
117        gbc.gridx = 0;
118        gbc.anchor = GridBagConstraints.NORTHWEST;
119        gbc.fill = GridBagConstraints.HORIZONTAL;
120        gbc.weightx = 1.0;
121
122        int row = -1;
123        for (final PluginInformation pi : displayedPlugins) {
124            boolean selected = model.isSelectedPlugin(pi.getName());
125            String remoteversion = formatPluginRemoteVersion(pi);
126            String localversion = formatPluginLocalVersion(model.getPluginInformation(pi.getName()));
127
128            final PluginCheckBox cbPlugin = new PluginCheckBox(pi, selected, this, model);
129            String pluginText = tr("{0}: Version {1} (local: {2})", pi.getName(), remoteversion, localversion);
130            if (pi.requires != null && !pi.requires.isEmpty()) {
131                pluginText += tr(" (requires: {0})", pi.requires);
132            }
133            JLabel lblPlugin = new JLabel(
134                    pluginText,
135                    pi.getScaledIcon(),
136                    SwingConstants.LEFT);
137            lblPlugin.addMouseListener(new PluginCheckBoxMouseAdapter(cbPlugin));
138
139            gbc.gridx = 0;
140            gbc.gridy = ++row;
141            gbc.insets = new Insets(5, 5, 0, 5);
142            gbc.weighty = 0.0;
143            gbc.weightx = 0.0;
144            add(cbPlugin, gbc);
145
146            gbc.gridx = 1;
147            gbc.weightx = 1.0;
148            add(lblPlugin, gbc);
149
150            HtmlPanel description = new HtmlPanel();
151            description.setText(pi.getDescriptionAsHtml());
152            description.enableClickableHyperlinks();
153            lblPlugin.setLabelFor(description);
154
155            gbc.gridx = 1;
156            gbc.gridy = ++row;
157            gbc.insets = new Insets(3, 25, 5, 5);
158            gbc.weighty = 1.0;
159            add(description, gbc);
160        }
161    }
162
163    /**
164     * Refreshes the list.
165     */
166    public void refreshView() {
167        final Rectangle visibleRect = getVisibleRect();
168        List<PluginInformation> displayedPlugins = model.getDisplayedPlugins();
169        removeAll();
170
171        if (displayedPlugins.isEmpty()) {
172            displayEmptyPluginListInformation();
173        } else {
174            displayPluginList(displayedPlugins);
175        }
176        revalidate();
177        repaint();
178        SwingUtilities.invokeLater(() -> scrollRectToVisible(visibleRect));
179    }
180}