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.HashSet;
011import java.util.Set;
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 = new HashSet<>();
062            for (PluginInformation p : ppModel.getAvailablePlugins()) {
063                if (!p.equals(pi) && p.requires != null && ppModel.isSelectedPlugin(p.getName())) {
064                    for (String s : p.getRequiredPlugins()) {
065                        if (s.equals(pi.getName())) {
066                            otherPlugins.add(p.getName());
067                            break;
068                        }
069                    }
070                }
071            }
072            if (!otherPlugins.isEmpty()) {
073                alertPluginStillRequired(panel, pi.getName(), otherPlugins);
074            }
075        }
076    }
077
078    /**
079     * Alerts the user if an unselected plugin is still required by another plugins
080     *
081     * @param parent The parent Component used to display error popup
082     * @param plugin the plugin
083     * @param otherPlugins the other plugins
084     */
085    private static void alertPluginStillRequired(Component parent, String plugin, Set<String> otherPlugins) {
086        StringBuilder sb = new StringBuilder("<html>")
087          .append(trn("Plugin {0} is still required by this plugin:",
088                "Plugin {0} is still required by these {1} plugins:",
089                otherPlugins.size(),
090                Utils.escapeReservedCharactersHTML(plugin),
091                otherPlugins.size()))
092          .append(Utils.joinAsHtmlUnorderedList(otherPlugins))
093          .append("</html>");
094        JOptionPane.showMessageDialog(
095                parent,
096                sb.toString(),
097                tr("Warning"),
098                JOptionPane.WARNING_MESSAGE
099        );
100    }
101}