/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.plugins;
import com.intellij.CommonBundle;
import com.intellij.icons.AllIcons;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.xml.util.XmlStringUtil;
import consulo.annotations.RequiredDispatchThread;
import consulo.ide.updateSettings.impl.PlatformOrPluginUpdateResult;
import consulo.ide.updateSettings.impl.PluginListDialog;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* @author lloix
*/
public class InstallPluginAction extends AnAction implements DumbAware {
private static final Set<IdeaPluginDescriptor> ourInstallingNodes = new HashSet<>();
private final PluginManagerMain myInstalledPluginPanel;
private final PluginManagerMain myAvailablePluginPanel;
public InstallPluginAction(PluginManagerMain availablePluginPanel, PluginManagerMain installedPluginPanel) {
super(IdeBundle.message("action.download.and.install.plugin"), IdeBundle.message("action.download.and.install.plugin"), AllIcons.Actions.Install);
myAvailablePluginPanel = availablePluginPanel;
myInstalledPluginPanel = installedPluginPanel;
}
@RequiredDispatchThread
@Override
public void update(@NotNull AnActionEvent e) {
Presentation presentation = e.getPresentation();
IdeaPluginDescriptor[] selection = getPluginTable().getSelectedObjects();
boolean enabled = (selection != null);
if (enabled) {
for (IdeaPluginDescriptor descr : selection) {
presentation.setText(IdeBundle.message("action.download.and.install.plugin"));
presentation.setDescription(IdeBundle.message("action.download.and.install.plugin"));
enabled &= !ourInstallingNodes.contains(descr);
if (descr instanceof PluginNode) {
enabled &= !PluginManagerColumnInfo.isDownloaded((PluginNode)descr);
if (((PluginNode)descr).getStatus() == PluginNode.STATUS_INSTALLED) {
presentation.setText(IdeBundle.message("action.update.plugin"));
presentation.setDescription(IdeBundle.message("action.update.plugin"));
enabled &= InstalledPluginsTableModel.hasNewerVersion(descr.getPluginId());
}
}
else if (descr instanceof IdeaPluginDescriptorImpl) {
presentation.setText(IdeBundle.message("action.update.plugin"));
presentation.setDescription(IdeBundle.message("action.update.plugin"));
PluginId id = descr.getPluginId();
enabled = enabled && InstalledPluginsTableModel.hasNewerVersion(id);
}
}
}
presentation.setEnabled(enabled);
}
@RequiredDispatchThread
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
install(e.getProject(), null);
}
@RequiredDispatchThread
public void install(@Nullable Project project, @Nullable final Runnable onSuccess) {
IdeaPluginDescriptor[] selection = getPluginTable().getSelectedObjects();
final List<IdeaPluginDescriptor> list = new ArrayList<>();
for (IdeaPluginDescriptor descr : selection) {
PluginNode pluginNode = null;
if (descr instanceof PluginNode) {
pluginNode = (PluginNode)descr;
}
else if (descr instanceof IdeaPluginDescriptorImpl) {
final PluginId pluginId = descr.getPluginId();
pluginNode = new PluginNode(pluginId);
pluginNode.setName(descr.getName());
pluginNode.addDependency(descr.getDependentPluginIds());
pluginNode.addOptionalDependency(descr.getOptionalDependentPluginIds());
pluginNode.setSize("-1");
}
if (pluginNode != null) {
list.add(pluginNode);
ourInstallingNodes.add(pluginNode);
}
final InstalledPluginsTableModel pluginsModel = (InstalledPluginsTableModel)myInstalledPluginPanel.getPluginsModel();
final Set<IdeaPluginDescriptor> disabled = new HashSet<>();
final Set<IdeaPluginDescriptor> disabledDependants = new HashSet<>();
for (IdeaPluginDescriptor node : list) {
final PluginId pluginId = node.getPluginId();
if (pluginsModel.isDisabled(pluginId)) {
disabled.add(node);
}
for (PluginId dependantId : node.getDependentPluginIds()) {
final IdeaPluginDescriptor pluginDescriptor = PluginManager.getPlugin(dependantId);
if (pluginDescriptor != null && pluginsModel.isDisabled(dependantId)) {
disabledDependants.add(pluginDescriptor);
}
}
}
if (suggestToEnableInstalledPlugins(pluginsModel, disabled, disabledDependants, list)) {
myInstalledPluginPanel.setRequireShutdown(true);
}
}
final Consumer<Collection<IdeaPluginDescriptor>> afterCallback = pluginNodes -> {
if (pluginNodes.isEmpty()) {
return;
}
UIUtil.invokeLaterIfNeeded(() -> installedPluginsToModel(pluginNodes));
if (!myInstalledPluginPanel.isDisposed()) {
UIUtil.invokeLaterIfNeeded(() -> {
getPluginTable().updateUI();
myInstalledPluginPanel.setRequireShutdown(true);
});
}
else {
boolean needToRestart = false;
for (IdeaPluginDescriptor node : pluginNodes) {
final IdeaPluginDescriptor pluginDescriptor = PluginManager.getPlugin(node.getPluginId());
if (pluginDescriptor == null || pluginDescriptor.isEnabled()) {
needToRestart = true;
break;
}
}
if (needToRestart) {
PluginManagerMain.notifyPluginsWereInstalled(pluginNodes, null);
}
}
if (onSuccess != null) {
onSuccess.run();
}
ourInstallingNodes.removeAll(pluginNodes);
};
downloadAndInstallPlugins(project, list, myAvailablePluginPanel.getPluginsModel().getAllPlugins(), afterCallback);
}
public static boolean downloadAndInstallPlugins(@Nullable Project project,
@NotNull final List<IdeaPluginDescriptor> toInstall,
@NotNull final List<IdeaPluginDescriptor> allPlugins,
@Nullable final Consumer<Collection<IdeaPluginDescriptor>> afterCallback) {
Set<IdeaPluginDescriptor> pluginsForInstallWithDependencies = PluginInstallUtil.getPluginsForInstall(toInstall, allPlugins);
PlatformOrPluginUpdateResult result = new PlatformOrPluginUpdateResult(PlatformOrPluginUpdateResult.Type.PLUGIN_INSTALL,
pluginsForInstallWithDependencies.stream().map(x -> Couple.of(x, x))
.collect(Collectors.toList()));
Predicate<PluginId> greenNodeStrategy = pluginId -> {
// do not mark target node as green, only depend
for (IdeaPluginDescriptor node : toInstall) {
if (node.getPluginId().equals(pluginId)) {
return false;
}
}
return true;
};
PluginListDialog dialog = new PluginListDialog(project, result, greenNodeStrategy, afterCallback);
if (pluginsForInstallWithDependencies.size() == toInstall.size()) {
dialog.doOKAction();
return true;
}
else {
return dialog.showAndGet();
}
}
private static boolean suggestToEnableInstalledPlugins(final InstalledPluginsTableModel pluginsModel,
final Set<IdeaPluginDescriptor> disabled,
final Set<IdeaPluginDescriptor> disabledDependants,
final List<IdeaPluginDescriptor> list) {
if (!disabled.isEmpty() || !disabledDependants.isEmpty()) {
String message = "";
if (disabled.size() == 1) {
message += "Updated plugin '" + disabled.iterator().next().getName() + "' is disabled.";
}
else if (!disabled.isEmpty()) {
message += "Updated plugins " + StringUtil.join(disabled, IdeaPluginDescriptor::getName, ", ") + " are disabled.";
}
if (!disabledDependants.isEmpty()) {
message += "<br>";
message += "Updated plugin" + (list.size() > 1 ? "s depend " : " depends ") + "on disabled";
if (disabledDependants.size() == 1) {
message += " plugin '" + disabledDependants.iterator().next().getName() + "'.";
}
else {
message += " plugins " + StringUtil.join(disabledDependants, IdeaPluginDescriptor::getName, ", ") + ".";
}
}
message += " Disabled plugins and plugins which depends on disabled plugins won't be activated after restart.";
int result;
if (!disabled.isEmpty() && !disabledDependants.isEmpty()) {
result = Messages.showYesNoCancelDialog(XmlStringUtil.wrapInHtml(message), CommonBundle.getWarningTitle(), "Enable all",
"Enable updated plugin" + (disabled.size() > 1 ? "s" : ""), CommonBundle.getCancelButtonText(),
Messages.getQuestionIcon());
if (result == Messages.CANCEL) return false;
}
else {
message += "<br>Would you like to enable ";
if (!disabled.isEmpty()) {
message += "updated plugin" + (disabled.size() > 1 ? "s" : "");
}
else {
//noinspection SpellCheckingInspection
message += "plugin dependenc" + (disabledDependants.size() > 1 ? "ies" : "y");
}
message += "?</body></html>";
result = Messages.showYesNoDialog(message, CommonBundle.getWarningTitle(), Messages.getQuestionIcon());
if (result == Messages.NO) return false;
}
if (result == Messages.YES) {
disabled.addAll(disabledDependants);
pluginsModel.enableRows(disabled.toArray(new IdeaPluginDescriptor[disabled.size()]), true);
}
else if (result == Messages.NO && !disabled.isEmpty()) {
pluginsModel.enableRows(disabled.toArray(new IdeaPluginDescriptor[disabled.size()]), true);
}
return true;
}
return false;
}
private void installedPluginsToModel(Collection<IdeaPluginDescriptor> list) {
for (IdeaPluginDescriptor pluginNode : list) {
final String idString = pluginNode.getPluginId().getIdString();
final PluginManagerUISettings pluginManagerUISettings = PluginManagerUISettings.getInstance();
if (!pluginManagerUISettings.getInstalledPlugins().contains(idString)) {
pluginManagerUISettings.getInstalledPlugins().add(idString);
}
pluginManagerUISettings.myOutdatedPlugins.remove(idString);
}
final InstalledPluginsTableModel installedPluginsModel = (InstalledPluginsTableModel)myInstalledPluginPanel.getPluginsModel();
for (IdeaPluginDescriptor node : list) {
installedPluginsModel.appendOrUpdateDescriptor(node);
}
}
public PluginTable getPluginTable() {
return myAvailablePluginPanel.getPluginTable();
}
}