diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsState.java b/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsState.java index 621bed7d5fd2..638ca670af64 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsState.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsState.java @@ -34,6 +34,7 @@ public final class InstalledPluginsState { private final Map myInstalledPlugins = ContainerUtil.newIdentityHashMap(); private final Map myInstalledWithoutRestartPlugins = ContainerUtil.newIdentityHashMap(); private final Map myUpdatedPlugins = ContainerUtil.newIdentityHashMap(); + private final Map myUninstalledWithoutRestartPlugins = ContainerUtil.newIdentityHashMap(); private final Set myOutdatedPlugins = new SmartHashSet<>(); @NotNull @@ -55,6 +56,18 @@ public final class InstalledPluginsState { } } + public boolean wasInstalledWithoutRestart(@NotNull PluginId id) { + synchronized (myLock) { + return myInstalledWithoutRestartPlugins.containsKey(id); + } + } + + public boolean wasUninstalledWithoutRestart(@NotNull PluginId id) { + synchronized (myLock) { + return myUninstalledWithoutRestartPlugins.containsKey(id); + } + } + public boolean wasUpdated(@NotNull PluginId id) { synchronized (myLock) { return myUpdatedPlugins.containsKey(id); @@ -85,7 +98,6 @@ public final class InstalledPluginsState { } } } - /** * Should be called whenever a new plugin is installed or an existing one is updated. */ @@ -104,4 +116,18 @@ public final class InstalledPluginsState { } } } + + + public void onPluginUninstall(@NotNull IdeaPluginDescriptor descriptor, boolean restartNeeded) { + PluginId id = descriptor.getPluginId(); + synchronized (myLock) { + if (!restartNeeded) { + myUninstalledWithoutRestartPlugins.put(id, descriptor); + } + } + } + + public void resetChangesAppliedWithoutRestart() { + myInstalledWithoutRestartPlugins.clear(); + } } diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java index 90f707288fd8..4d3e0d9c09ab 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java @@ -5,6 +5,7 @@ import com.intellij.CommonBundle; import com.intellij.ide.startup.StartupActionScriptManager; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; @@ -41,6 +42,8 @@ import java.util.zip.ZipFile; * @author stathik */ public class PluginInstaller { + private static final Logger LOG = Logger.getInstance(PluginInstaller.class); + public static final String UNKNOWN_HOST_MARKER = "__unknown_repository__"; static final Object ourLock = new Object(); @@ -74,7 +77,7 @@ public class PluginInstaller { else { boolean needRestart = !DynamicPlugins.isUnloadSafe(pluginDescriptor); if (needRestart) { - StartupActionScriptManager.addActionCommand(new StartupActionScriptManager.DeleteCommand(pluginDescriptor.getPath())); + uninstallAfterRestart(pluginDescriptor); } PluginStateManager.fireState(pluginDescriptor, false); @@ -85,13 +88,24 @@ public class PluginInstaller { return false; } + private static void uninstallAfterRestart(IdeaPluginDescriptor pluginDescriptor) throws IOException { + StartupActionScriptManager.addActionCommand(new StartupActionScriptManager.DeleteCommand(pluginDescriptor.getPath())); + } + public static boolean uninstallDynamicPlugin(IdeaPluginDescriptor pluginDescriptor) { - boolean uninstalledWithoutRestart; - uninstalledWithoutRestart = DynamicPlugins.unloadPlugin((IdeaPluginDescriptorImpl)pluginDescriptor); + boolean uninstalledWithoutRestart = DynamicPlugins.unloadPlugin((IdeaPluginDescriptorImpl)pluginDescriptor); if (uninstalledWithoutRestart) { FileUtil.delete(pluginDescriptor.getPath()); PluginManagerCore.setPlugins(ArrayUtil.remove(PluginManagerCore.getPlugins(), pluginDescriptor)); } + else { + try { + uninstallAfterRestart(pluginDescriptor); + } + catch (IOException e) { + LOG.error(e); + } + } return uninstalledWithoutRestart; } @@ -110,7 +124,8 @@ public class PluginInstaller { commands.add(new StartupActionScriptManager.CopyCommand(sourceFile, new File(pluginsPath, sourceFile.getName()))); } else { - commands.add(new StartupActionScriptManager.DeleteCommand(new File(pluginsPath, rootEntryName(sourceFile)))); // drops stale directory + commands + .add(new StartupActionScriptManager.DeleteCommand(new File(pluginsPath, rootEntryName(sourceFile)))); // drops stale directory commands.add(new StartupActionScriptManager.UnzipCommand(sourceFile, new File(pluginsPath))); } @@ -232,7 +247,7 @@ public class PluginInstaller { IdeaPluginDescriptorImpl targetDescriptor = PluginManagerCore.loadDescriptor(targetFile, PluginManagerCore.PLUGIN_XML); if (targetDescriptor != null) { DynamicPlugins.loadPlugin(targetDescriptor); - PluginManagerCore.setPlugins(ArrayUtil.mergeArrays(PluginManagerCore.getPlugins(), new IdeaPluginDescriptor[] { targetDescriptor })); + PluginManagerCore.setPlugins(ArrayUtil.mergeArrays(PluginManagerCore.getPlugins(), new IdeaPluginDescriptor[]{targetDescriptor})); } } } @@ -257,7 +272,8 @@ public class PluginInstaller { } if (!notInstalled.isEmpty()) { String deps = StringUtil.join(notInstalled, PluginId::toString, ", "); - String message = "Plugin " + pluginDescriptor.getName() + " depends on unknown plugin" + (notInstalled.size() > 1 ? "s " : " ") + deps; + String message = + "Plugin " + pluginDescriptor.getName() + " depends on unknown plugin" + (notInstalled.size() > 1 ? "s " : " ") + deps; MessagesEx.showWarningDialog(parent, message, "Install Plugin"); } if (!disabledIds.isEmpty()) { @@ -270,7 +286,9 @@ public class PluginInstaller { String part = "disabled plugin" + (dependencies.size() > 1 ? "s " : " "); String deps = StringUtil.join(dependencies, IdeaPluginDescriptor::getName, ", "); String message = "Plugin " + pluginDescriptor.getName() + " depends on " + part + deps + ". Enable " + part.trim() + "?"; - if (Messages.showOkCancelDialog(message, "Install Plugin", "Install", CommonBundle.getCancelButtonText(), Messages.getWarningIcon()) == Messages.OK) { + if (Messages + .showOkCancelDialog(message, "Install Plugin", "Install", CommonBundle.getCancelButtonText(), Messages.getWarningIcon()) == + Messages.OK) { model.enableRows(dependencies.toArray(new IdeaPluginDescriptor[0]), Boolean.TRUE); } } @@ -288,7 +306,8 @@ public class PluginInstaller { descriptor.setTitle("Choose Plugin File"); descriptor.setDescription("JAR and ZIP archives are accepted"); final String oldPath = PropertiesComponent.getInstance().getValue(PLUGINS_PRESELECTION_PATH); - final VirtualFile toSelect = oldPath == null ? null : VfsUtil.findFileByIoFile(new File(FileUtil.toSystemDependentName(oldPath)), false); + final VirtualFile toSelect = + oldPath == null ? null : VfsUtil.findFileByIoFile(new File(FileUtil.toSystemDependentName(oldPath)), false); FileChooser.chooseFile(descriptor, null, parent, toSelect, virtualFile -> { File file = VfsUtilCore.virtualToIoFile(virtualFile); PropertiesComponent.getInstance().setValue(PLUGINS_PRESELECTION_PATH, FileUtil.toSystemIndependentName(file.getParent())); diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java index 3b49578e0429..2efecd58489f 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java @@ -1586,6 +1586,8 @@ public class PluginManagerConfigurable myShutdownCallback.run(); myShutdownCallback = null; } + + InstalledPluginsState.getInstance().resetChangesAppliedWithoutRestart(); } @Override diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/newui/CellPluginComponent.java b/platform/platform-impl/src/com/intellij/ide/plugins/newui/CellPluginComponent.java index ea13221f8d19..fff29d9675ec 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/newui/CellPluginComponent.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/newui/CellPluginComponent.java @@ -201,7 +201,7 @@ public abstract class CellPluginComponent extends JPanel { public abstract void showProgress(); - public abstract void hideProgress(boolean success); + public abstract void hideProgress(boolean success, boolean restartRequired); public void clearProgress() { throw new UnsupportedOperationException(); diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/newui/GridCellPluginComponent.java b/platform/platform-impl/src/com/intellij/ide/plugins/newui/GridCellPluginComponent.java index ec4fe79f03e6..bcc13d42c7f8 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/newui/GridCellPluginComponent.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/newui/GridCellPluginComponent.java @@ -177,7 +177,7 @@ public class GridCellPluginComponent extends CellPluginComponent { } @Override - public void hideProgress(boolean success) { + public void hideProgress(boolean success, boolean restartRequired) { myIndicator = null; JComponent lastComponent = myLastComponent; if (success) { diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/newui/InstallButton.java b/platform/platform-impl/src/com/intellij/ide/plugins/newui/InstallButton.java index 5a800fab6b16..1a72e08a0b67 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/newui/InstallButton.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/newui/InstallButton.java @@ -3,6 +3,7 @@ package com.intellij.ide.plugins.newui; import com.intellij.ide.plugins.PluginManagerConfigurable; import com.intellij.ui.JBColor; +import org.jetbrains.annotations.Nullable; import java.awt.*; @@ -48,13 +49,17 @@ public class InstallButton extends ColorButton { } @Override - public void setEnabled(boolean enabled) { + public void setEnabled(boolean b) { + super.setEnabled(b); + } + + public void setEnabled(boolean enabled, @Nullable String statusText) { super.setEnabled(enabled); if (enabled) { setTextAndSize(); } else { - setText("Installed"); + setText(statusText); setWidth(this, 80); } } diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/newui/ListPluginComponent.java b/platform/platform-impl/src/com/intellij/ide/plugins/newui/ListPluginComponent.java index 9348e82ba660..8d5c98b84af4 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/newui/ListPluginComponent.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/newui/ListPluginComponent.java @@ -45,7 +45,7 @@ public class ListPluginComponent extends CellPluginComponent { private final JLabel myIconComponent = new JLabel(AllIcons.Plugins.PluginLogo_40); private final BaselineLayout myLayout = new BaselineLayout(); private JButton myRestartButton; - private JButton myInstallButton; + private InstallButton myInstallButton; private JButton myUpdateButton; private JCheckBox myEnableDisableButton; private JLabel myRating; @@ -105,7 +105,7 @@ public class ListPluginComponent extends CellPluginComponent { myLayout.addButtonComponent(myInstallButton = new InstallButton(false)); myInstallButton.addActionListener(e -> myPluginModel.installOrUpdatePlugin(myPlugin, null)); - myInstallButton.setEnabled(PluginManagerCore.getPlugin(myPlugin.getPluginId()) == null); + myInstallButton.setEnabled(PluginManagerCore.getPlugin(myPlugin.getPluginId()) == null, "Installed"); ColorButton.setWidth72(myInstallButton); } } @@ -390,12 +390,17 @@ public class ListPluginComponent extends CellPluginComponent { } @Override - public void hideProgress(boolean success) { + public void hideProgress(boolean success, boolean restartRequired) { myIndicator = null; myLayout.removeProgressComponent(); if (success) { - enableRestart(); + if (restartRequired) { + enableRestart(); + } + else { + myInstallButton.setEnabled(false, "Installed"); + } } fullRepaint(); diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/newui/MyPluginModel.java b/platform/platform-impl/src/com/intellij/ide/plugins/newui/MyPluginModel.java index e044efe1125d..51743f730a7c 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/newui/MyPluginModel.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/newui/MyPluginModel.java @@ -152,7 +152,7 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM } } - return applyEnableDisablePlugins(enabledMap) || needRestartForUninstall; + return applyEnableDisablePlugins(enabledMap) && !needRestartForUninstall; } private boolean applyEnableDisablePlugins(Map enabledMap) { @@ -407,13 +407,13 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM List gridComponents = myGridMap.get(descriptor); if (gridComponents != null) { for (CellPluginComponent gridComponent : gridComponents) { - gridComponent.hideProgress(success); + gridComponent.hideProgress(success, restartRequired); } } List listComponents = myListMap.get(descriptor); if (listComponents != null) { for (CellPluginComponent listComponent : listComponents) { - listComponent.hideProgress(success); + listComponent.hideProgress(success, restartRequired); } } for (PluginDetailsPageComponent panel : myDetailPanels) { @@ -537,7 +537,7 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM for (Entry> entry : myGridMap.entrySet()) { if (id.equals(entry.getKey().getPluginId().getIdString())) { for (CellPluginComponent component : entry.getValue()) { - component.hideProgress(true); + component.hideProgress(true, true); } break; } @@ -769,6 +769,7 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM try { ((IdeaPluginDescriptorImpl)descriptor).setDeleted(true); needRestartForUninstall = PluginInstaller.prepareToUninstall(descriptor.getPluginId()); + InstalledPluginsState.getInstance().onPluginUninstall(descriptor, needRestartForUninstall); if (!needRestartForUninstall) { myDynamicPluginsToUninstall.add(descriptor); } @@ -794,8 +795,8 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM } for (PluginDetailsPageComponent panel : myDetailPanels) { - if (panel.myPlugin == descriptor && needRestartForUninstall) { - panel.enableRestart(); + if (panel.myPlugin == descriptor) { + panel.updateButtons(); } } } diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginDetailsPageComponent.java b/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginDetailsPageComponent.java index 308518a0f4ba..74b5119f0aa6 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginDetailsPageComponent.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginDetailsPageComponent.java @@ -50,7 +50,7 @@ public class PluginDetailsPageComponent extends MultiPanel { private final JEditorPane myNameComponent = createNameComponent(); private final BaselinePanel myNameAndButtons = new BaselinePanel(); private JButton myRestartButton; - private JButton myInstallButton; + private InstallButton myInstallButton; private JButton myUpdateButton; private JButton myEnableDisableButton; private JBOptionButton myEnableDisableUninstallButton; @@ -345,56 +345,7 @@ public class PluginDetailsPageComponent extends MultiPanel { myNameComponent.setText("" + myPlugin.getName() + ""); updateIcon(); - if (myMarketplace) { - boolean installed = InstalledPluginsState.getInstance().wasInstalled(myPlugin.getPluginId()); - myRestartButton.setVisible(installed); - - myInstallButton.setEnabled(PluginManagerCore.getPlugin(myPlugin.getPluginId()) == null); - myInstallButton.setVisible(!installed); - - myUpdateButton.setVisible(false); - myEnableDisableButton.setVisible(false); - myEnableDisableUninstallButton.setVisible(false); - myUninstallButton.setVisible(false); - } - else { - myInstallButton.setVisible(false); - - boolean restart = myPlugin instanceof IdeaPluginDescriptorImpl && ((IdeaPluginDescriptorImpl)myPlugin).isDeleted(); - if (!restart) { - InstalledPluginsState pluginsState = InstalledPluginsState.getInstance(); - PluginId id = myPlugin.getPluginId(); - restart = pluginsState.wasInstalled(id) || pluginsState.wasUpdated(id); - } - - if (restart) { - myRestartButton.setVisible(true); - myUpdateButton.setVisible(false); - myEnableDisableButton.setVisible(false); - myEnableDisableUninstallButton.setVisible(false); - myUninstallButton.setVisible(false); - } - else { - myRestartButton.setVisible(false); - - boolean bundled = myPlugin.isBundled(); - String title = myPluginModel.getEnabledTitle(myPlugin); - boolean errors = myPluginModel.hasErrors(myPlugin); - - myUpdateButton.setVisible(myUpdateDescriptor != null && !errors); - - myEnableDisableButton.setVisible(bundled && !errors); - myEnableDisableButton.setText(title); - - myEnableDisableUninstallButton.setVisible(!bundled && !errors); - myEnableDisableUninstallButton.setText(title); - - myUninstallButton.setVisible(!bundled && errors); - } - - updateEnableForNameAndIcon(); - updateErrors(); - } + updateButtons(); boolean bundled = myPlugin.isBundled() && !myPlugin.allowBundledUpdate(); String version = bundled ? "bundled" : myPlugin.getVersion(); @@ -500,6 +451,68 @@ public class PluginDetailsPageComponent extends MultiPanel { } } + public void updateButtons() { + if (myMarketplace) { + boolean installed = InstalledPluginsState.getInstance().wasInstalled(myPlugin.getPluginId()); + boolean installedWithoutRestart = InstalledPluginsState.getInstance().wasInstalledWithoutRestart(myPlugin.getPluginId()); + myRestartButton.setVisible(installed); + + myInstallButton.setEnabled(PluginManagerCore.getPlugin(myPlugin.getPluginId()) == null && !installedWithoutRestart, "Installed"); + myInstallButton.setVisible(!installed); + + myUpdateButton.setVisible(false); + myEnableDisableButton.setVisible(false); + myEnableDisableUninstallButton.setVisible(false); + myUninstallButton.setVisible(false); + } + else { + myInstallButton.setVisible(false); + + boolean uninstalled = myPlugin instanceof IdeaPluginDescriptorImpl && ((IdeaPluginDescriptorImpl)myPlugin).isDeleted(); + boolean uninstalledWithoutRestart = InstalledPluginsState.getInstance().wasUninstalledWithoutRestart(myPlugin.getPluginId()); + if (!uninstalled) { + InstalledPluginsState pluginsState = InstalledPluginsState.getInstance(); + PluginId id = myPlugin.getPluginId(); + uninstalled = pluginsState.wasInstalled(id) || pluginsState.wasUpdated(id); + } + + if (uninstalled) { + if (uninstalledWithoutRestart) { + myRestartButton.setVisible(false); + myInstallButton.setVisible(true); + myInstallButton.setEnabled(false, "Uninstalled"); + } + else { + myRestartButton.setVisible(true); + } + myUpdateButton.setVisible(false); + myEnableDisableButton.setVisible(false); + myEnableDisableUninstallButton.setVisible(false); + myUninstallButton.setVisible(false); + } + else { + myRestartButton.setVisible(false); + + boolean bundled = myPlugin.isBundled(); + String title = myPluginModel.getEnabledTitle(myPlugin); + boolean errors = myPluginModel.hasErrors(myPlugin); + + myUpdateButton.setVisible(myUpdateDescriptor != null && !errors); + + myEnableDisableButton.setVisible(bundled && !errors); + myEnableDisableButton.setText(title); + + myEnableDisableUninstallButton.setVisible(!bundled && !errors); + myEnableDisableUninstallButton.setText(title); + + myUninstallButton.setVisible(!bundled && errors); + } + + updateEnableForNameAndIcon(); + updateErrors(); + } + } + private void updateIcon() { boolean jb = PluginManagerConfigurable.isJBPlugin(myPlugin); boolean errors = myPluginModel.hasErrors(myPlugin); @@ -551,7 +564,7 @@ public class PluginDetailsPageComponent extends MultiPanel { myNameAndButtons.removeProgressComponent(); if (success) { - enableRestart(); + updateButtons(); } if (repaint) { fullRepaint(); @@ -598,15 +611,6 @@ public class PluginDetailsPageComponent extends MultiPanel { } } - public void enableRestart() { - myInstallButton.setVisible(false); - myUpdateButton.setVisible(false); - myEnableDisableButton.setVisible(false); - myEnableDisableUninstallButton.setVisible(false); - myUninstallButton.setVisible(false); - myRestartButton.setVisible(true); - } - @Nullable private String getDescription() { String description = myPlugin.getDescription();