From e6de8d8c64e5e2fbacf43c9abd7bc2475427239e Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Thu, 19 Dec 2019 20:49:25 +0300 Subject: [PATCH] IDEA-214866 Plugins Update dialog GitOrigin-RevId: 2f7d41a5ea45c1148c4172b75de6aaa7fe3f2d66 --- .../plugins/PluginManagerConfigurable.java | 27 +- .../ide/plugins/newui/BaselinePanel.java | 10 + .../ide/plugins/newui/InstallPluginInfo.java | 2 +- .../plugins/newui/ListPluginComponent.java | 48 ++- .../ide/plugins/newui/MyPluginModel.java | 3 +- .../newui/PluginDetailsPageComponent.java | 12 +- .../plugins/newui/PluginsGroupComponent.java | 16 +- .../PluginsGroupComponentWithProgress.java | 5 +- .../updateSettings/impl/PluginDownloader.java | 2 +- .../impl/PluginUpdateDialog.java | 360 ++++++++++++++++++ .../updateSettings/impl/UpdateChecker.kt | 5 +- .../src/messages/IdeBundle.properties | 1 + 12 files changed, 442 insertions(+), 49 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateDialog.java 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 613bfdf0fcca..de68a9a36863 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java @@ -64,6 +64,8 @@ import java.util.*; import java.util.function.Consumer; import java.util.function.Function; +import static java.lang.System.out; + /** * @author Alexander Lobas */ @@ -104,9 +106,6 @@ public class PluginManagerConfigurable private SearchResultPanel myMarketplaceSearchPanel; private SearchResultPanel myInstalledSearchPanel; - private LinkListener myNameListener; - private LinkListener mySearchListener; - private final LinkLabel myUpdateAll = new LinkLabel<>("Update All", null); private final JLabel myUpdateCounter = new CountComponent(); private final CountIcon myCountIcon = new CountIcon(); @@ -198,13 +197,6 @@ public class PluginManagerConfigurable }); myPluginModel.setPluginUpdatesService(myPluginUpdatesService); - myNameListener = (aSource, aLinkData) -> { - // TODO: unused - }; - mySearchListener = (aSource, aLinkData) -> { - // TODO: unused - }; - createMarketplaceTab(); createInstalledTab(); @@ -359,8 +351,7 @@ public class PluginManagerConfigurable protected JComponent createPluginsPanel(@NotNull Consumer selectionListener) { MultiSelectionEventHandler eventHandler = new MultiSelectionEventHandler(); myMarketplacePanel = - new PluginsGroupComponentWithProgress(new PluginListLayout(), eventHandler, myNameListener, - PluginManagerConfigurable.this.mySearchListener, + new PluginsGroupComponentWithProgress(new PluginListLayout(), eventHandler, d -> new ListPluginComponent(myPluginModel, d, mySearchListener, true)); myMarketplacePanel.setSelectionListener(selectionListener); @@ -623,10 +614,8 @@ public class PluginManagerConfigurable marketplaceController.setSearchResultEventHandler(eventHandler); PluginsGroupComponentWithProgress panel = - new PluginsGroupComponentWithProgress(new PluginListLayout(), eventHandler, myNameListener, - PluginManagerConfigurable.this.mySearchListener, - descriptor -> new ListPluginComponent(myPluginModel, descriptor, mySearchListener, - true)); + new PluginsGroupComponentWithProgress(new PluginListLayout(), eventHandler, + descriptor -> new ListPluginComponent(myPluginModel, descriptor, mySearchListener, true)); panel.setSelectionListener(selectionListener); registerCopyProvider(panel); @@ -775,8 +764,7 @@ public class PluginManagerConfigurable protected JComponent createPluginsPanel(@NotNull Consumer selectionListener) { MultiSelectionEventHandler eventHandler = new MultiSelectionEventHandler(); myInstalledPanel = - new PluginsGroupComponent(new PluginListLayout(), eventHandler, myNameListener, - PluginManagerConfigurable.this.mySearchListener, + new PluginsGroupComponent(new PluginListLayout(), eventHandler, descriptor -> new ListPluginComponent(myPluginModel, descriptor, mySearchListener, false)); myInstalledPanel.setSelectionListener(selectionListener); @@ -922,8 +910,7 @@ public class PluginManagerConfigurable installedController.setSearchResultEventHandler(eventHandler); PluginsGroupComponent panel = - new PluginsGroupComponent(new PluginListLayout(), eventHandler, myNameListener, - PluginManagerConfigurable.this.mySearchListener, + new PluginsGroupComponent(new PluginListLayout(), eventHandler, descriptor -> new ListPluginComponent(myPluginModel, descriptor, mySearchListener, false)); panel.setSelectionListener(selectionListener); diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/newui/BaselinePanel.java b/platform/platform-impl/src/com/intellij/ide/plugins/newui/BaselinePanel.java index d8c7effa59e7..4d9847790242 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/newui/BaselinePanel.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/newui/BaselinePanel.java @@ -157,6 +157,16 @@ public class BaselinePanel extends NonOpaquePanel { add(component, null); } + public void removeButtons() { + List buttons = new ArrayList<>(myButtonComponents); + myButtonComponents.clear(); + myButtonEnableStates = null; + + for (Component button : buttons) { + remove(button); + } + } + public void setProgressComponent(@Nullable ListPluginComponent pluginComponent, @NotNull JComponent progressComponent) { assert myProgressComponent == null; myProgressComponent = progressComponent; diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/newui/InstallPluginInfo.java b/platform/platform-impl/src/com/intellij/ide/plugins/newui/InstallPluginInfo.java index b29455eeed83..f754850cb3e3 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/newui/InstallPluginInfo.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/newui/InstallPluginInfo.java @@ -54,7 +54,7 @@ public class InstallPluginInfo { if (myPluginModel == null) { MyPluginModel.finishInstall(myDescriptor); closeStatusBarIndicator(); - if (success && restartRequired) { + if (success && !cancel && restartRequired) { ApplicationManager.getApplication().invokeLater(() -> PluginManagerConfigurable.shutdownOrRestartApp()); } } 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 081729d628ef..f0c394bed9bd 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,6 +45,7 @@ public class ListPluginComponent extends JPanel { private final boolean myMarketplace; public IdeaPluginDescriptor myPlugin; private boolean myUninstalled; + private boolean myOnlyUpdateMode; public IdeaPluginDescriptor myUpdateDescriptor; private final JLabel myNameComponent = new JLabel(); @@ -149,7 +150,8 @@ public class ListPluginComponent extends JPanel { else { myLayout.addButtonComponent(myInstallButton = new InstallButton(false)); - myInstallButton.addActionListener(e -> myPluginModel.installOrUpdatePlugin(myPlugin, null, ModalityState.stateForComponent(myInstallButton))); + myInstallButton + .addActionListener(e -> myPluginModel.installOrUpdatePlugin(myPlugin, null, ModalityState.stateForComponent(myInstallButton))); myInstallButton.setEnabled(PluginManagerCore.getPlugin(myPlugin.getPluginId()) == null, "Installed"); ColorButton.setWidth72(myInstallButton); } @@ -295,6 +297,17 @@ public class ListPluginComponent extends JPanel { } } + public void setOnlyUpdateMode(@Nullable IdeaPluginDescriptor descriptor) { + myOnlyUpdateMode = true; + + if (myEnableDisableButton != null) { + myLayout.removeButtonComponent(myEnableDisableButton); + myEnableDisableButton = null; + } + + setUpdateDescriptor(descriptor); + } + public void setUpdateDescriptor(@Nullable IdeaPluginDescriptor descriptor) { if (myUpdateDescriptor == null && descriptor == null) { return; @@ -337,9 +350,11 @@ public class ListPluginComponent extends JPanel { } if (myUpdateButton == null) { myLayout.addButtonComponent(myUpdateButton = new UpdateButton(), 0); - myUpdateButton.addActionListener(e -> myPluginModel.installOrUpdatePlugin(myPlugin, myUpdateDescriptor, ModalityState.stateForComponent(myUpdateButton))); + myUpdateButton.addActionListener( + e -> myPluginModel.installOrUpdatePlugin(myPlugin, myUpdateDescriptor, ModalityState.stateForComponent(myUpdateButton))); } else { + myUpdateButton.setEnabled(true); myUpdateButton.setVisible(true); } if (myAlignButton != null) { @@ -350,9 +365,7 @@ public class ListPluginComponent extends JPanel { doLayout(); } - public void setListeners(@NotNull LinkListener listener, - @NotNull LinkListener searchListener, - @NotNull EventHandler eventHandler) { + public void setListeners(@NotNull EventHandler eventHandler) { myEventHandler = eventHandler; eventHandler.addAll(this); } @@ -520,7 +533,7 @@ public class ListPluginComponent extends JPanel { } public void updatePlugin() { - if (!myMarketplace && myUpdateButton != null && myUpdateButton.isVisible()) { + if (!myMarketplace && myUpdateButton != null && myUpdateButton.isVisible() && myUpdateButton.isEnabled()) { myUpdateButton.doClick(); } } @@ -533,6 +546,18 @@ public class ListPluginComponent extends JPanel { return myMarketplace; } + public boolean isRestartEnabled() { + return myRestartButton != null && myRestartButton.isVisible(); + } + + public boolean isUpdatedWithoutRestart() { + return myUpdateButton != null && myUpdateButton.isVisible() && !myUpdateButton.isEnabled(); + } + + public boolean underProgress() { + return myIndicator != null; + } + public void close() { if (myIndicator != null) { MyPluginModel.removeProgress(myPlugin, myIndicator); @@ -581,7 +606,7 @@ public class ListPluginComponent extends JPanel { for (int i = 0; i < size; i++) { JButton button = selection.get(i).myUpdateButton; - if (button == null || !button.isVisible()) { + if (button == null || !button.isVisible() || !button.isEnabled()) { updateButtons = null; break; } @@ -595,6 +620,10 @@ public class ListPluginComponent extends JPanel { } } + if (myOnlyUpdateMode) { + return; + } + Pair result = getSelectionNewState(selection); group.add(new MyAnAction(result.first ? "Enable" : "Disable", null, KeyEvent.VK_SPACE) { @Override @@ -663,7 +692,7 @@ public class ListPluginComponent extends JPanel { boolean update = true; for (ListPluginComponent component : selection) { JButton button = component.myUpdateButton; - if (button == null || !button.isVisible()) { + if (button == null || !button.isVisible() || !button.isEnabled()) { update = false; break; } @@ -680,6 +709,9 @@ public class ListPluginComponent extends JPanel { } } else if (!restart && !update) { + if (myOnlyUpdateMode) { + return; + } if (keyCode == KeyEvent.VK_SPACE) { if (selection.size() == 1) { myPluginModel.changeEnableDisable(selection.get(0).myPlugin); 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 63796fb34df8..50e47d2134ad 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 @@ -422,10 +422,11 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM }); } - public void toBackground() { + public boolean toBackground() { for (InstallPluginInfo info : myInstallingInfos.values()) { info.toBackground(myStatusBar); } + return !myInstallingInfos.isEmpty(); } @NotNull 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 1dbbda2363e9..eff7d53df31f 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 @@ -187,10 +187,12 @@ public class PluginDetailsPageComponent extends MultiPanel { myNameAndButtons.addButtonComponent(myRestartButton = new RestartButton(myPluginModel)); myNameAndButtons.addButtonComponent(myUpdateButton = new UpdateButton()); - myUpdateButton.addActionListener(e -> myPluginModel.installOrUpdatePlugin(myPlugin, myUpdateDescriptor, ModalityState.stateForComponent(myUpdateButton))); + myUpdateButton.addActionListener( + e -> myPluginModel.installOrUpdatePlugin(myPlugin, myUpdateDescriptor, ModalityState.stateForComponent(myUpdateButton))); myNameAndButtons.addButtonComponent(myInstallButton = new InstallButton(true)); - myInstallButton.addActionListener(e -> myPluginModel.installOrUpdatePlugin(myPlugin, null, ModalityState.stateForComponent(myInstallButton))); + myInstallButton + .addActionListener(e -> myPluginModel.installOrUpdatePlugin(myPlugin, null, ModalityState.stateForComponent(myInstallButton))); myEnableDisableButton = new JButton(); myEnableDisableButton.addActionListener(e -> myPluginModel.changeEnableDisable(myPlugin)); @@ -216,6 +218,12 @@ public class PluginDetailsPageComponent extends MultiPanel { } } + public void setOnlyUpdateMode() { + myNameAndButtons.removeButtons(); + myPanel.setBorder(JBUI.Borders.empty(15, 20, 0, 0)); + myEmptyPanel.setBorder(null); + } + private void createMetricsPanel(@NotNull JPanel centerPanel) { // text field without horizontal margins myVersion = new JTextField(); diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginsGroupComponent.java b/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginsGroupComponent.java index 0b4caf352eed..58a5e6244e87 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginsGroupComponent.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginsGroupComponent.java @@ -5,7 +5,6 @@ import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManagerConfigurable; import com.intellij.ui.JBColor; import com.intellij.ui.components.JBPanelWithEmptyText; -import com.intellij.ui.components.labels.LinkListener; import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.ui.components.panels.OpaquePanel; import com.intellij.ui.scale.JBUIScale; @@ -28,20 +27,14 @@ import java.util.function.Consumer; */ public class PluginsGroupComponent extends JBPanelWithEmptyText { private final EventHandler myEventHandler; - private final LinkListener myListener; - private final LinkListener mySearchListener; private final Function myFunction; private final List myGroups = new ArrayList<>(); public PluginsGroupComponent(@NotNull LayoutManager layout, @NotNull EventHandler eventHandler, - @NotNull LinkListener listener, - @NotNull LinkListener searchListener, @NotNull Function function) { super(layout); myEventHandler = eventHandler; - myListener = listener; - mySearchListener = searchListener; myFunction = function; myEventHandler.connect(this); @@ -195,13 +188,16 @@ public class PluginsGroupComponent extends JBPanelWithEmptyText { return -1; } - private void addToGroup(@NotNull PluginsGroup group, @NotNull List descriptors, int index, int eventIndex) { + private void addToGroup(@NotNull PluginsGroup group, + @NotNull List descriptors, + int index, + int eventIndex) { for (IdeaPluginDescriptor descriptor : descriptors) { ListPluginComponent pluginComponent = myFunction.fun(descriptor); group.ui.plugins.add(pluginComponent); add(pluginComponent, index); myEventHandler.addCell(pluginComponent, eventIndex); - pluginComponent.setListeners(myListener, mySearchListener, myEventHandler); + pluginComponent.setListeners(myEventHandler); if (index != -1) { index++; } @@ -233,7 +229,7 @@ public class PluginsGroupComponent extends JBPanelWithEmptyText { group.ui.plugins.add(index, pluginComponent); add(pluginComponent, uiIndex); myEventHandler.addCell(pluginComponent, anchor); - pluginComponent.setListeners(myListener, mySearchListener, myEventHandler); + pluginComponent.setListeners(myEventHandler); } public void removeGroup(@NotNull PluginsGroup group) { diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginsGroupComponentWithProgress.java b/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginsGroupComponentWithProgress.java index 38464da7b914..b2aa2226a651 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginsGroupComponentWithProgress.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginsGroupComponentWithProgress.java @@ -3,7 +3,6 @@ package com.intellij.ide.plugins.newui; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.openapi.util.Disposer; -import com.intellij.ui.components.labels.LinkListener; import com.intellij.util.Function; import com.intellij.util.ui.AsyncProcessIcon; import org.jetbrains.annotations.NotNull; @@ -20,10 +19,8 @@ public class PluginsGroupComponentWithProgress extends PluginsGroupComponent { public PluginsGroupComponentWithProgress(@NotNull LayoutManager layout, @NotNull EventHandler eventHandler, - @NotNull LinkListener listener, - @NotNull LinkListener searchListener, @NotNull Function function) { - super(layout, eventHandler, listener, searchListener, function); + super(layout, eventHandler, function); myIcon.setOpaque(false); myIcon.setPaintPassiveIcon(false); add(myIcon); diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java index 01de9a885dad..6d2524cb27ff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginDownloader.java @@ -58,7 +58,7 @@ public final class PluginDownloader { private boolean myShownErrors; - private PluginDownloader(IdeaPluginDescriptor descriptor, String url, BuildNumber buildNumber) { + private PluginDownloader(@NotNull IdeaPluginDescriptor descriptor, @NotNull String url, @Nullable BuildNumber buildNumber) { myPluginId = descriptor.getPluginId(); myPluginName = descriptor.getName(); myProductCode = descriptor.getProductCode(); diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateDialog.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateDialog.java new file mode 100644 index 000000000000..2be3dca97c34 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateDialog.java @@ -0,0 +1,360 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.openapi.updateSettings.impl; + +import com.intellij.CommonBundle; +import com.intellij.ide.IdeBundle; +import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.ide.plugins.PluginManagerConfigurable; +import com.intellij.ide.plugins.PluginManagerCore; +import com.intellij.ide.plugins.PluginManagerMain; +import com.intellij.ide.plugins.newui.*; +import com.intellij.notification.NotificationType; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.ex.ApplicationManagerEx; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.progress.PerformInBackgroundOption; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Divider; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.OnePixelSplitter; +import com.intellij.ui.components.labels.LinkListener; +import com.intellij.ui.components.panels.OpaquePanel; +import com.intellij.util.LineSeparator; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.JBDimension; +import com.intellij.util.ui.JBUI; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.*; + +/** + * @author Alexander Lobas + */ +public class PluginUpdateDialog extends DialogWrapper { + private final Collection myDownloaders; + + private final MyPluginModel myPluginModel = new MyPluginModel() { + }; + private final PluginsGroupComponent myPluginsPanel; + private final PluginsGroup myGroup; + private final PluginDetailsPageComponent myDetailsPage; + + private final Action myIgnoreAction; + + public PluginUpdateDialog(@NotNull Collection uploadedPlugins) { + super(true); + setTitle("Plugin Updates"); + + myDownloaders = uploadedPlugins; + + myIgnoreAction = new AbstractAction( + IdeBundle.message(uploadedPlugins.size() == 1 ? "updates.ignore.update.button" : "updates.ignore.updates.button")) { + @Override + public void actionPerformed(ActionEvent e) { + close(CANCEL_EXIT_CODE); + ignorePlugins(ContainerUtil.map(myGroup.ui.plugins, component -> component.myUpdateDescriptor)); + } + }; + + myPluginModel.setTopController(new Configurable.TopComponentController() { + @Override + public void setLeftComponent(@Nullable Component component) { + } + + @Override + public void showProgress(boolean start) { + } + }); + myPluginModel.setPluginUpdatesService(new PluginUpdatesService() { + @Override + public void finishUpdate(@NotNull IdeaPluginDescriptor descriptor) { + updateButtons(); + } + + @Override + public void finishUpdate() { + updateButtons(); + } + }); + + myDetailsPage = new PluginDetailsPageComponent(myPluginModel, emptyListener(), false) { + @Override + public void showProgress() { + } + }; + myDetailsPage.setOnlyUpdateMode(); + + MultiSelectionEventHandler eventHandler = new MultiSelectionEventHandler(); + + myPluginsPanel = new PluginsGroupComponent(new PluginListLayout(), eventHandler, descriptor -> createListComponent(descriptor)); + PluginManagerConfigurable.registerCopyProvider(myPluginsPanel); + myPluginsPanel.setSelectionListener(__ -> { + List selection = myPluginsPanel.getSelection(); + int size = selection.size(); + myDetailsPage.showPlugin(size == 1 ? selection.get(0) : null, size > 1); + }); + + myGroup = new PluginsGroup("Plugin updates available"); + for (PluginDownloader plugin : uploadedPlugins) { + myGroup.descriptors.add(plugin.getDescriptor()); + } + myGroup.sortByName(); + myPluginsPanel.addGroup(myGroup); + + setOKButtonText(false, false); + setCancelButtonText(IdeBundle.message("updates.remind.later.button")); + init(); + + JRootPane rootPane = getPeer().getRootPane(); + if (rootPane != null) { + rootPane.setPreferredSize(new JBDimension(800, 600)); + } + } + + private void updateButtons() { + int count = myGroup.ui.plugins.size(); + int restart = 0; + int progress = 0; + int updatedWithoutRestart = 0; + for (ListPluginComponent plugin : myGroup.ui.plugins) { + if (plugin.isRestartEnabled()) { + restart++; + } + else if (plugin.underProgress()) { + progress++; + } + else if (plugin.isUpdatedWithoutRestart()) { + updatedWithoutRestart++; + } + } + + setOKButtonText(restart + progress > 0, updatedWithoutRestart == count); + getCancelAction().setEnabled(restart + updatedWithoutRestart < count); + myIgnoreAction.setEnabled(restart + progress + updatedWithoutRestart == 0); + } + + private void setOKButtonText(boolean restart, boolean close) { + if (close) { + setOKButtonText("Close"); + } + else { + String action = ApplicationManager.getApplication().isRestartCapable() ? "Restart" : "Shutdown"; + setOKButtonText(restart ? action + " IDE" : "Update All and " + action); + } + } + + @Override + protected void doOKAction() { + super.doOKAction(); + + List toDownloads = new ArrayList<>(); + List toIgnore = new ArrayList<>(); + int index = 0; + boolean restart = false; + + for (PluginDownloader downloader : myDownloaders) { + ListPluginComponent component = myGroup.ui.plugins.get(index++); + if (component.isRestartEnabled() || component.underProgress()) { + restart = true; + } + else if (!component.isUpdatedWithoutRestart()) { + toDownloads.add(downloader); + toIgnore.add(component.myUpdateDescriptor); + } + } + + boolean background = myPluginModel.toBackground(); + + if (toDownloads.size() != myDownloaders.size() || background) { + if (!toIgnore.isEmpty()) { + ignorePlugins(toIgnore); + } + if (!background && restart) { + ApplicationManager.getApplication().invokeLater(() -> ApplicationManagerEx.getApplicationEx().restart(true)); + } + return; + } + + new Task.Backgroundable(null, IdeBundle.message("update.notifications.title"), true, PerformInBackgroundOption.DEAF) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + final List downloaders = UpdateInstaller.downloadPluginUpdates(toDownloads, indicator); + if (!downloaders.isEmpty()) { + ApplicationManager.getApplication().invokeLater(() -> { + final PluginUpdateResult result = UpdateInstaller.installDownloadedPluginUpdates(downloaders, getContentPanel(), true); + if (result.getPluginsInstalled().size() > 0) { + if (result.getRestartRequired()) { + PluginManagerMain.notifyPluginsUpdated(null); + } + else { + String message; + if (result.getPluginsInstalled().size() == 1) { + final IdeaPluginDescriptor installedPlugin = result.getPluginsInstalled().get(0); + message = "Updated " + installedPlugin.getName() + " plugin to version " + installedPlugin.getVersion(); + } + else { + message = "Updated " + result.getPluginsInstalled() + " plugins"; + } + UpdateChecker.NOTIFICATIONS.createNotification(message, NotificationType.INFORMATION).notify(myProject); + } + } + }); + } + } + }.queue(); + } + + @Override + public void doCancelAction() { + super.doCancelAction(); + + if (myPluginModel.toBackground()) { + return; + } + + for (ListPluginComponent plugin : myGroup.ui.plugins) { + if (plugin.isRestartEnabled()) { + ApplicationManager.getApplication().invokeLater(() -> PluginManagerConfigurable.shutdownOrRestartApp()); + return; + } + } + } + + @NotNull + @Override + protected Action[] createLeftSideActions() { + return ContainerUtil.ar(myIgnoreAction); + } + + @NotNull + @Override + protected DialogStyle getStyle() { + return DialogStyle.COMPACT; + } + + @Override + protected String getDimensionServiceKey() { + return "#com.intellij.openapi.updateSettings.impl.PluginUpdateInfoDialog"; + } + + @NotNull + private ListPluginComponent createListComponent(IdeaPluginDescriptor updateDescriptor) { + IdeaPluginDescriptor descriptor = PluginManagerCore.getPlugin(updateDescriptor.getPluginId()); + assert descriptor != null; + ListPluginComponent component = new ListPluginComponent(myPluginModel, descriptor, emptyListener(), false) { + @Override + public void showProgress() { + super.showProgress(); + updateButtons(); + } + }; + component.setOnlyUpdateMode(updateDescriptor); + return component; + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + OnePixelSplitter splitter = new OnePixelSplitter(false, 0.45f) { + @Override + protected Divider createDivider() { + Divider divider = super.createDivider(); + divider.setBackground(PluginManagerConfigurable.SEARCH_FIELD_BORDER_COLOR); + return divider; + } + }; + + JPanel leftPanel = new JPanel(new BorderLayout()); + leftPanel.add(PluginManagerConfigurable.createScrollPane(myPluginsPanel, true)); + + OpaquePanel titlePanel = new OpaquePanel(new BorderLayout(), PluginManagerConfigurable.MAIN_BG_COLOR); + titlePanel.setBorder(JBUI.Borders.empty(6, 10)); + leftPanel.add(titlePanel, BorderLayout.SOUTH); + + JLabel titleComponent = new JLabel("Plugins can be updated later in " + CommonBundle.settingsTitle() + " | Plugins"); + titleComponent.setForeground(PluginsGroupComponent.SECTION_HEADER_FOREGROUND); + titlePanel.add(titleComponent); + + ((JComponent)myGroup.ui.panel).setBorder(JBUI.Borders.empty(6, 10)); + + splitter.setFirstComponent(leftPanel); + splitter.setSecondComponent(myDetailsPage); + + return splitter; + } + + private static Set myIgnoredPluginsWithVersions; + private static final String DISABLED_UPDATE = "plugin_disabled_updates.txt"; + + @NotNull + private static File getDisabledUpdateFile() { + return new File(PathManager.getConfigPath(), DISABLED_UPDATE); + } + + @NotNull + private static Set getIgnoredPlugins() { + if (myIgnoredPluginsWithVersions == null) { + myIgnoredPluginsWithVersions = new HashSet<>(); + + if (!ApplicationManager.getApplication().isUnitTestMode()) { + try { + File file = getDisabledUpdateFile(); + if (file.isFile()) { + myIgnoredPluginsWithVersions.addAll(FileUtil.loadLines(file)); + } + } + catch (IOException e) { + Logger.getInstance(UpdateChecker.class).error(e); + } + } + } + return myIgnoredPluginsWithVersions; + } + + private static void ignorePlugins(@NotNull List descriptors) { + Set ignoredPlugins = getIgnoredPlugins(); + + for (IdeaPluginDescriptor descriptor : descriptors) { + ignoredPlugins.add(getIdVersionValue(descriptor)); + } + + try { + FileUtil + .writeToFile(getDisabledUpdateFile(), StringUtil.join(ignoredPlugins, LineSeparator.getSystemLineSeparator().getSeparatorString())); + } + catch (IOException e) { + Logger.getInstance(UpdateChecker.class).error(e); + } + } + + public static boolean isIgnored(@NotNull IdeaPluginDescriptor descriptor) { + Set plugins = getIgnoredPlugins(); + if (plugins.isEmpty()) { + return false; + } + return plugins.contains(getIdVersionValue(descriptor)); + } + + @NotNull + private static String getIdVersionValue(@NotNull IdeaPluginDescriptor descriptor) { + return descriptor.getPluginId().getIdString() + "+" + descriptor.getVersion(); + } + + @NotNull + private static LinkListener emptyListener() { + return (__, ___) -> { + }; + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt index 1474c5ad548a..12ec07e60487 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateChecker.kt @@ -147,7 +147,8 @@ object UpdateChecker { val updatedPlugins: Collection? val externalUpdates: Collection? try { - updatedPlugins = checkPluginsUpdate(indicator, incompatiblePlugins, buildNumber) + updatedPlugins = checkPluginsUpdate(indicator, incompatiblePlugins, + buildNumber)?.filter { downloader -> !PluginUpdateDialog.isIgnored(downloader.descriptor) } externalUpdates = checkExternalUpdates(showDialog, updateSettings, indicator) } catch (e: IOException) { @@ -395,7 +396,7 @@ object UpdateChecker { if (updatedPlugins != null && !updatedPlugins.isEmpty()) { updateFound = true - val runnable = { PluginUpdateInfoDialog(updatedPlugins, showSettingsLink).show() } + val runnable = { PluginUpdateDialog(updatedPlugins).show() } ourShownNotifications.remove(NotificationUniqueType.PLUGINS)?.forEach { it.expire() } diff --git a/platform/platform-resources-en/src/messages/IdeBundle.properties b/platform/platform-resources-en/src/messages/IdeBundle.properties index e87567369785..1ecec491616f 100644 --- a/platform/platform-resources-en/src/messages/IdeBundle.properties +++ b/platform/platform-resources-en/src/messages/IdeBundle.properties @@ -968,6 +968,7 @@ updates.incompatible.plugins.found={0,choice,1#Plugin|2#Plugins} incompatible wi updates.download.and.restart.button=Up&date and Restart updates.apply.manually.button=Up&date Manually updates.ignore.update.button=&Ignore This Update +updates.ignore.updates.button=&Ignore These Updates updates.remind.later.button=&Remind Me Later updates.new.version.available=A new version of {0} is available! updates.current.version=Current version: