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}