001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.preferences.plugin;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005import static org.openstreetmap.josm.tools.I18n.trn;
006
007import java.awt.Component;
008import java.awt.event.ActionEvent;
009import java.awt.event.ActionListener;
010import java.util.Set;
011import java.util.stream.Collectors;
012
013import javax.swing.JCheckBox;
014import javax.swing.JOptionPane;
015
016import org.openstreetmap.josm.plugins.PluginHandler;
017import org.openstreetmap.josm.plugins.PluginInformation;
018import org.openstreetmap.josm.tools.Utils;
019
020/**
021 * A plugin checkbox.
022 * @since 10228
023 */
024public class PluginCheckBox extends JCheckBox implements ActionListener {
025    private final transient PluginInformation pi;
026    private final PluginListPanel panel;
027    private final transient PluginPreferencesModel ppModel;
028
029    PluginCheckBox(PluginInformation pi, boolean selected, PluginListPanel panel, PluginPreferencesModel ppModel) {
030        this.pi = pi;
031        this.panel = panel;
032        this.ppModel = ppModel;
033        setSelected(selected);
034        setToolTipText(PluginListPanel.formatCheckboxTooltipText(pi));
035        addActionListener(this);
036    }
037
038    protected void selectRequiredPlugins(PluginInformation info) {
039        if (info != null && info.requires != null) {
040            for (String s : info.getRequiredPlugins()) {
041                if (!ppModel.isSelectedPlugin(s)) {
042                    ppModel.setPluginSelected(s, true);
043                    selectRequiredPlugins(ppModel.getPluginInformation(s));
044                }
045            }
046        }
047    }
048
049    @Override
050    public void actionPerformed(ActionEvent e) {
051        // Select/unselect corresponding plugin in the model
052        ppModel.setPluginSelected(pi.getName(), isSelected());
053        // Does the newly selected plugin require other plugins ?
054        if (isSelected() && pi.requires != null) {
055            // Select required plugins
056            selectRequiredPlugins(pi);
057            // Alert user if plugin requirements are not met
058            PluginHandler.checkRequiredPluginsPreconditions(panel, ppModel.getAvailablePlugins(), pi, false);
059        } else if (!isSelected()) {
060            // If the plugin has been unselected, was it required by other plugins still selected ?
061            Set<String> otherPlugins = ppModel.getAvailablePlugins().stream()
062                    .filter(p -> !p.equals(pi) && p.requires != null && ppModel.isSelectedPlugin(p.getName()))
063                    .filter(p -> p.getRequiredPlugins().stream().anyMatch(s -> s.equals(pi.getName()) || s.equals(pi.provides)))
064                    .map(PluginInformation::getName)
065                    .collect(Collectors.toSet());
066            if (!otherPlugins.isEmpty()) {
067                alertPluginStillRequired(panel, pi.getName(), otherPlugins);
068            }
069        }
070    }
071
072    /**
073     * Alerts the user if an unselected plugin is still required by another plugins
074     *
075     * @param parent The parent Component used to display error popup
076     * @param plugin the plugin
077     * @param otherPlugins the other plugins
078     */
079    private static void alertPluginStillRequired(Component parent, String plugin, Set<String> otherPlugins) {
080        StringBuilder sb = new StringBuilder("<html>")
081          .append(trn("Plugin {0} is still required by this plugin:",
082                "Plugin {0} is still required by these {1} plugins:",
083                otherPlugins.size(),
084                Utils.escapeReservedCharactersHTML(plugin),
085                otherPlugins.size()))
086          .append(Utils.joinAsHtmlUnorderedList(otherPlugins))
087          .append("</html>");
088        JOptionPane.showMessageDialog(
089                parent,
090                sb.toString(),
091                tr("Warning"),
092                JOptionPane.WARNING_MESSAGE
093        );
094    }
095}