diff --git a/platform/icons/src/ide/notification/ideUpdate.svg b/platform/icons/src/ide/notification/ideUpdate.svg
new file mode 100644
index 000000000000..d89e9fa052bd
--- /dev/null
+++ b/platform/icons/src/ide/notification/ideUpdate.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/platform/icons/src/ide/notification/ideUpdate_dark.svg b/platform/icons/src/ide/notification/ideUpdate_dark.svg
new file mode 100644
index 000000000000..12ee6b26bc10
--- /dev/null
+++ b/platform/icons/src/ide/notification/ideUpdate_dark.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/platform/icons/src/ide/notification/pluginUpdate.svg b/platform/icons/src/ide/notification/pluginUpdate.svg
new file mode 100644
index 000000000000..3ea25b928998
--- /dev/null
+++ b/platform/icons/src/ide/notification/pluginUpdate.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/platform/icons/src/ide/notification/pluginUpdate_dark.svg b/platform/icons/src/ide/notification/pluginUpdate_dark.svg
new file mode 100644
index 000000000000..35033e96f9a5
--- /dev/null
+++ b/platform/icons/src/ide/notification/pluginUpdate_dark.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/platform/platform-api/resources/messages/IdeBundle.properties b/platform/platform-api/resources/messages/IdeBundle.properties
index be53f290fc20..c680ed943b9b 100644
--- a/platform/platform-api/resources/messages/IdeBundle.properties
+++ b/platform/platform-api/resources/messages/IdeBundle.properties
@@ -2403,8 +2403,19 @@ wsl.linux.distribution.label=Linux &distribution:
wsl.no.available.distributions=No available distributions
name.variable=File name entered in the dialog
+action.plugins.text=Plugins...
+settings.entry.point.tooltip=IDE and Project Settings
+settings.entry.point.update.tooltip=Updates available. IDE and Project Settings
+settings.entry.point.update.ide.action=Download {0} {1}
+settings.entry.point.update.plugin.action=Update {0} Plugin...
+settings.entry.point.update.plugins.action=Update Plugins...
+settings.entry.point.update.plugins.more.action=and {0} more
template.file.name=Template to generate file name and path
template.file.name.optional=Template to generate file name and path (optional)
updates.settings.show.editor=Show What's New in the editor after an IDE update
plugin.version.bundled=bundled
commit.step.user.cancelled=User cancelled
+laf.action.install.theme=Install Theme...
+keymap.action.configure.keymap=Configure Keymap...
+keymap.action.install.keymap=Install Keymap...
+settings.entry.point.got.it.popup=Quickly access the main IDE and project settings, and execute commands.
diff --git a/platform/platform-api/src/com/intellij/notification/NotificationType.java b/platform/platform-api/src/com/intellij/notification/NotificationType.java
index 1981ad829000..23edef107b55 100644
--- a/platform/platform-api/src/com/intellij/notification/NotificationType.java
+++ b/platform/platform-api/src/com/intellij/notification/NotificationType.java
@@ -19,6 +19,7 @@ package com.intellij.notification;
* @author spleaner
*/
public enum NotificationType {
+ IDE_UPDATE,
INFORMATION,
WARNING,
ERROR
diff --git a/platform/platform-api/src/com/intellij/openapi/wm/WelcomeScreenTab.java b/platform/platform-api/src/com/intellij/openapi/wm/WelcomeScreenTab.java
index a9f3dc474997..acc99f0ecd3c 100644
--- a/platform/platform-api/src/com/intellij/openapi/wm/WelcomeScreenTab.java
+++ b/platform/platform-api/src/com/intellij/openapi/wm/WelcomeScreenTab.java
@@ -15,7 +15,7 @@ public interface WelcomeScreenTab {
* @return component presents list item on the {@link WelcomeScreen} tab view
*/
@NotNull
- JComponent getKeyComponent();
+ JComponent getKeyComponent(@NotNull JList extends WelcomeScreenTab> list);
/**
* @return component shown when related key component is selected
diff --git a/platform/platform-impl/src/com/intellij/ide/actions/QuickChangeKeymapAction.java b/platform/platform-impl/src/com/intellij/ide/actions/QuickChangeKeymapAction.java
index c022c44de775..95c680f86cca 100644
--- a/platform/platform-impl/src/com/intellij/ide/actions/QuickChangeKeymapAction.java
+++ b/platform/platform-impl/src/com/intellij/ide/actions/QuickChangeKeymapAction.java
@@ -2,6 +2,7 @@
package com.intellij.ide.actions;
import com.intellij.icons.AllIcons;
+import com.intellij.ide.IdeBundle;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
@@ -10,7 +11,9 @@ import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
import com.intellij.openapi.keymap.impl.KeymapManagerImpl;
import com.intellij.openapi.keymap.impl.KeymapManagerImplKt;
+import com.intellij.openapi.keymap.impl.ui.KeymapPanel;
import com.intellij.openapi.keymap.impl.ui.KeymapSchemeManager;
+import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
@@ -28,6 +31,14 @@ public final class QuickChangeKeymapAction extends QuickSwitchSchemeAction {
for (Keymap keymap : list) {
addKeymapAction(group, manager, current, keymap);
}
+ group.addSeparator();
+ group.add(new DumbAwareAction(IdeBundle.message("keymap.action.configure.keymap")) {
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ ShowSettingsUtil.getInstance().showSettingsDialog(e.getProject(), KeymapPanel.class);
+ }
+ });
+ group.add(new ShowPluginsWithSearchOptionAction(IdeBundle.message("keymap.action.install.keymap"), "/tag:Keymap"));
}
@NotNull
diff --git a/platform/platform-impl/src/com/intellij/ide/actions/QuickChangeLookAndFeel.java b/platform/platform-impl/src/com/intellij/ide/actions/QuickChangeLookAndFeel.java
index 273af2380149..535c27591b30 100644
--- a/platform/platform-impl/src/com/intellij/ide/actions/QuickChangeLookAndFeel.java
+++ b/platform/platform-impl/src/com/intellij/ide/actions/QuickChangeLookAndFeel.java
@@ -2,6 +2,7 @@
package com.intellij.ide.actions;
import com.intellij.icons.AllIcons;
+import com.intellij.ide.IdeBundle;
import com.intellij.ide.ui.LafManager;
import com.intellij.ide.ui.LafManagerListener;
import com.intellij.ide.ui.laf.darcula.DarculaInstaller;
@@ -27,6 +28,7 @@ import javax.swing.*;
public class QuickChangeLookAndFeel extends QuickSwitchSchemeAction {
private UIManager.LookAndFeelInfo initialLaf;
private final Alarm switchAlarm = new Alarm();
+ private boolean myLafActionSelection;
@Override
protected void fillActions(Project project, @NotNull DefaultActionGroup group, @NotNull DataContext dataContext) {
@@ -37,6 +39,9 @@ public class QuickChangeLookAndFeel extends QuickSwitchSchemeAction {
for (UIManager.LookAndFeelInfo lf : lfs) {
group.add(new LafChangeAction(lf, initialLaf == lf));
}
+
+ group.addSeparator();
+ group.add(new ShowPluginsWithSearchOptionAction(IdeBundle.message("laf.action.install.theme"), "/tag:Theme"));
}
@Override
@@ -44,14 +49,17 @@ public class QuickChangeLookAndFeel extends QuickSwitchSchemeAction {
switchAlarm.cancelAllRequests();
if (Registry.is("ide.instant.theme.switch")) {
popup.addListSelectionListener(event -> {
- JList list = (JList)event.getSource();
- Object item = list.getSelectedValue();
+ Object item = ((JList)event.getSource()).getSelectedValue();
if (item instanceof AnActionHolder) {
- switchAlarm.cancelAllRequests();
- switchAlarm.addRequest(() -> {
- LafChangeAction action = (LafChangeAction)((AnActionHolder)item).getAction();
- switchLafAndUpdateUI(LafManager.getInstance(), action.myLookAndFeelInfo, false);
- }, Registry.get("ide.instant.theme.switch.delay").asInteger());
+ AnAction anAction = ((AnActionHolder)item).getAction();
+ myLafActionSelection = anAction instanceof LafChangeAction;
+ if (myLafActionSelection) {
+ switchAlarm.cancelAllRequests();
+ switchAlarm.addRequest(() -> {
+ LafChangeAction action = (LafChangeAction)anAction;
+ switchLafAndUpdateUI(LafManager.getInstance(), action.myLookAndFeelInfo, false);
+ }, Registry.get("ide.instant.theme.switch.delay").asInteger());
+ }
}
});
}
@@ -59,7 +67,7 @@ public class QuickChangeLookAndFeel extends QuickSwitchSchemeAction {
popup.addListener(new JBPopupListener() {
@Override
public void onClosed(@NotNull LightweightWindowEvent event) {
- if (Registry.is("ide.instant.theme.switch") && !event.isOk()) {
+ if (Registry.is("ide.instant.theme.switch") && !event.isOk() && myLafActionSelection) {
switchLafAndUpdateUI(LafManager.getInstance(), initialLaf, false);
}
}
diff --git a/platform/platform-impl/src/com/intellij/ide/actions/SettingsEntryPointAction.java b/platform/platform-impl/src/com/intellij/ide/actions/SettingsEntryPointAction.java
new file mode 100644
index 000000000000..6387b58b4787
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/ide/actions/SettingsEntryPointAction.java
@@ -0,0 +1,483 @@
+// Copyright 2000-2020 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.ide.actions;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.DataManager;
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import com.intellij.ide.plugins.PluginManagerCore;
+import com.intellij.ide.plugins.PluginStateListener;
+import com.intellij.ide.plugins.PluginStateManager;
+import com.intellij.ide.plugins.newui.PluginUpdatesService;
+import com.intellij.ide.ui.UISettings;
+import com.intellij.ide.ui.UISettingsListener;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
+import com.intellij.openapi.actionSystem.ex.TooltipDescriptionProvider;
+import com.intellij.openapi.actionSystem.impl.ActionButton;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.ui.popup.*;
+import com.intellij.openapi.updateSettings.impl.CheckForUpdateResult;
+import com.intellij.openapi.updateSettings.impl.PluginDownloader;
+import com.intellij.openapi.updateSettings.impl.PluginUpdateDialog;
+import com.intellij.openapi.updateSettings.impl.UpdateInfoDialog;
+import com.intellij.openapi.util.*;
+import com.intellij.openapi.wm.*;
+import com.intellij.openapi.wm.impl.status.IdeStatusBarImpl;
+import com.intellij.openapi.wm.impl.status.widget.StatusBarWidgetsManager;
+import com.intellij.ui.GotItTooltip;
+import com.intellij.util.Consumer;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.update.UiNotifyConnector;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author Alexander Lobas
+ */
+public class SettingsEntryPointAction extends AnAction implements DumbAware, RightAlignedToolbarAction, AnAction.TransparentUpdate,
+ TooltipDescriptionProvider, CustomComponentAction {
+ private Icon myIcon;
+
+ public SettingsEntryPointAction() {
+ initPluginsListeners();
+ }
+
+ @Override
+ public @NotNull JComponent createCustomComponent(@NotNull Presentation presentation, @NotNull String place) {
+ ActionButton button = new ActionButton(this, presentation, place, ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE);
+
+ UiNotifyConnector.doWhenFirstShown(button, () -> {
+ Disposable disposable = Disposer.newDisposable();
+
+ Balloon balloon = createGotItTooltip(disposable).showAt(Balloon.Position.below, button, component -> {
+ PropertiesComponent.getInstance().setValue(GotItTooltip.PROPERTY_PREFIX + ".settings.entry.point", "0");
+ return new Point(component.getWidth() / 2, component.getHeight());
+ });
+
+ if (balloon == null) {
+ Disposer.dispose(disposable);
+ }
+ else {
+ new MyListener(button.getParent(), balloon, disposable);
+ }
+ });
+
+ return button;
+ }
+
+ private static class MyListener extends ComponentAdapter implements HierarchyListener {
+ private final Component myComponent;
+ private final Balloon myBalloon;
+ private final Disposable myDisposable;
+
+ private MyListener(@NotNull Component component, @NotNull Balloon balloon, @NotNull Disposable disposable) {
+ myComponent = component;
+ myBalloon = balloon;
+ myDisposable = disposable;
+
+ myComponent.addComponentListener(this);
+ myComponent.addHierarchyListener(this);
+ }
+
+ @Override
+ public void componentResized(ComponentEvent e) {
+ handle();
+ }
+
+ @Override
+ public void componentMoved(ComponentEvent e) {
+ handle();
+ }
+
+ @Override
+ public void componentHidden(ComponentEvent e) {
+ handle();
+ }
+
+ @Override
+ public void hierarchyChanged(HierarchyEvent e) {
+ handle();
+ }
+
+ private void handle() {
+ if (myComponent.isShowing() && !myBalloon.isDisposed()) {
+ myBalloon.revalidate();
+ }
+ else {
+ myComponent.removeComponentListener(this);
+ myComponent.removeHierarchyListener(this);
+ Disposer.dispose(myDisposable);
+ }
+ }
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ myIcon = AllIcons.General.GearPlain;
+ // XXX update toolbar action
+
+ ListPopup popup = createMainPopup(e.getDataContext());
+ popup.addListener(new JBPopupListener() {
+ @Override
+ public void onClosed(@NotNull LightweightWindowEvent event) {
+ myIcon = null;
+ // XXX update toolbar action
+ }
+ });
+
+ InputEvent inputEvent = e.getInputEvent();
+ if (inputEvent == null) {
+ popup.showInFocusCenter();
+ }
+ else {
+ Component component = inputEvent.getComponent();
+ if (component instanceof ActionButtonComponent) {
+ popup.showUnderneathOf(component);
+ }
+ else {
+ popup.showInCenterOf(component);
+ }
+ }
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ Presentation presentation = e.getPresentation();
+ presentation.setDescription(SettingsEntryPointAction::getActionTooltip);
+ presentation.setIcon(myIcon == null ? getActionIcon() : myIcon);
+ }
+
+ @NotNull
+ private static ListPopup createMainPopup(@NotNull DataContext context) {
+ DefaultActionGroup group = new DefaultActionGroup();
+
+ if (myPlatformUpdateInfo != null) {
+ group.add(new DumbAwareAction(IdeBundle.message("settings.entry.point.update.ide.action",
+ ApplicationNamesInfo.getInstance().getFullProductName(),
+ myPlatformUpdateInfo.getNewBuild().getVersion()),
+ null, AllIcons.Ide.Notification.IdeUpdate) {
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ if (myPlatformUpdateInfo.getPatches() == null ||
+ (!SystemInfo.isWindows && !Files.isWritable(Paths.get(PathManager.getHomePath())))) {
+ new UpdateInfoDialog(myPlatformUpdateInfo.getUpdatedChannel(), myPlatformUpdateInfo.getNewBuild(), null, true, null,
+ myIncompatiblePlugins).show();
+ }
+ else {
+ CheckForUpdateResult platformUpdateInfo = myPlatformUpdateInfo;
+ newPlatformUpdate(null, null);
+
+ ActionCallback callback = new ActionCallback().doWhenRejected(() -> {
+ ApplicationManager.getApplication().invokeLater(() -> {
+ newPlatformUpdate(platformUpdateInfo, null);
+ }, ModalityState.any());
+ });
+
+ UpdateInfoDialog.downloadPatchAndRestart(platformUpdateInfo.getNewBuild(), platformUpdateInfo.getUpdatedChannel(),
+ platformUpdateInfo.getPatches(), null, null, callback);
+ }
+ }
+ });
+ }
+ if (myUpdatedPlugins != null) {
+ int size = myUpdatedPlugins.size();
+
+ String name = size == 1
+ ? IdeBundle.message("settings.entry.point.update.plugin.action", myUpdatedPlugins.iterator().next().getPluginName())
+ : IdeBundle.message("settings.entry.point.update.plugins.action");
+ group.add(new DumbAwareAction(name, null, AllIcons.Ide.Notification.PluginUpdate) {
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ new PluginUpdateDialog(e.getProject(), myUpdatedPlugins, myCustomRepositoryPlugins).show();
+ }
+ });
+ if (size > 1) {
+ StringBuilder result = new StringBuilder();
+ int count = 0;
+
+ for (PluginDownloader plugin : myUpdatedPlugins) {
+ if (result.length() > 0) {
+ result.append(", ");
+ }
+ result.append(plugin.getPluginName());
+ count++;
+ if (count == 2) {
+ int delta = size - count;
+ if (delta > 0) {
+ result.append(" ").append(IdeBundle.message("settings.entry.point.update.plugins.more.action", delta));
+ }
+ break;
+ }
+ }
+
+ @NlsSafe String actionName = result.toString();
+ group.add(new AnAction(actionName) {
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabled(false);
+ }
+ });
+ }
+ }
+
+ group.addSeparator();
+
+ ActionGroup templateGroup = (ActionGroup)ActionManager.getInstance().getAction("SettingsEntryPointGroup");
+ for (AnAction child : templateGroup.getChildren(null)) {
+ group.add(child);
+ }
+
+ return JBPopupFactory.getInstance().createActionGroupPopup(null, group, context, JBPopupFactory.ActionSelectionAid.MNEMONICS, true);
+ }
+
+ @NotNull
+ private static GotItTooltip createGotItTooltip(@NotNull Disposable disposable) {
+ return new GotItTooltip("settings.entry.point", IdeBundle.message("settings.entry.point.got.it.popup"), disposable);
+ }
+
+ private static PluginUpdatesService myUpdatesService;
+ private static PluginStateListener myPluginStateListener;
+
+ private static void initPluginsListeners() {
+ if (myUpdatesService == null) {
+ myUpdatesService = PluginUpdatesService.connectWithUpdates(descriptors -> {
+ List downloaders = new ArrayList<>();
+ try {
+ for (IdeaPluginDescriptor descriptor : descriptors) {
+ downloaders.add(PluginDownloader.createDownloader(descriptor));
+ }
+ }
+ catch (IOException e) {
+ PluginManagerCore.getLogger().error(e);
+ }
+ newPluginsUpdate(downloaders.isEmpty() ? null : downloaders, null);
+ });
+ }
+ if (myPluginStateListener == null) {
+ PluginStateManager.addStateListener(myPluginStateListener = new PluginStateListener() {
+ @Override
+ public void install(@NotNull IdeaPluginDescriptor descriptor) {
+ if (myUpdatedPlugins != null) {
+ PluginId pluginId = descriptor.getPluginId();
+ List updatedPlugins =
+ ContainerUtil.filter(myUpdatedPlugins, downloader -> !pluginId.equals(downloader.getId()));
+ if (myUpdatedPlugins.size() != updatedPlugins.size()) {
+ newPluginsUpdate(updatedPlugins.isEmpty() ? null : updatedPlugins, myCustomRepositoryPlugins);
+ }
+ }
+ }
+
+ @Override
+ public void uninstall(@NotNull IdeaPluginDescriptor descriptor) {
+ }
+ });
+ }
+ }
+
+ private static CheckForUpdateResult myPlatformUpdateInfo;
+ private static Collection myIncompatiblePlugins;
+
+ private static Collection myUpdatedPlugins;
+ private static Collection myCustomRepositoryPlugins;
+
+ public static void newPlatformUpdate(@Nullable CheckForUpdateResult platformUpdateInfo,
+ @Nullable Collection incompatiblePlugins) {
+ myPlatformUpdateInfo = platformUpdateInfo;
+ myIncompatiblePlugins = incompatiblePlugins;
+ updateAction();
+ }
+
+ public static void newPluginsUpdate(@Nullable Collection updatedPlugins,
+ @Nullable Collection customRepositoryPlugins) {
+ myUpdatedPlugins = updatedPlugins;
+ myCustomRepositoryPlugins = customRepositoryPlugins;
+ updateAction();
+ }
+
+ private static void updateAction() {
+ if (isAvailableInStatusBar()) {
+ updateWidgets();
+ }
+ // XXX update toolbar action
+ }
+
+ private static @NotNull @Nls String getActionTooltip() {
+ return myPlatformUpdateInfo == null && myUpdatedPlugins == null
+ ? IdeBundle.message("settings.entry.point.tooltip")
+ : IdeBundle.message("settings.entry.point.update.tooltip");
+ }
+
+ private static @NotNull Icon getActionIcon() {
+ if (myPlatformUpdateInfo != null) {
+ return AllIcons.Ide.Notification.IdeUpdate;
+ }
+ if (myUpdatedPlugins != null) {
+ return AllIcons.Ide.Notification.PluginUpdate;
+ }
+ return AllIcons.General.GearPlain;
+ }
+
+ private static UISettingsListener mySettingsListener;
+
+ private static void initUISettingsListener() {
+ if (mySettingsListener == null) {
+ mySettingsListener = uiSettings -> updateWidgets();
+ ApplicationManager.getApplication().getMessageBus().connect().subscribe(UISettingsListener.TOPIC, mySettingsListener);
+ }
+ }
+
+ private static void updateWidgets() {
+ for (Project project : ProjectManager.getInstance().getOpenProjects()) {
+ project.getService(StatusBarWidgetsManager.class).updateWidget(StatusBarManager.class);
+ IdeFrame frame = WindowManager.getInstance().getIdeFrame(project);
+ if (frame != null) {
+ StatusBar statusBar = frame.getStatusBar();
+ if (statusBar != null) {
+ statusBar.updateWidget(WIDGET_ID);
+ }
+ }
+ }
+ }
+
+ private static boolean isAvailableInStatusBar() {
+ initUISettingsListener();
+
+ UISettings settings = UISettings.getInstance();
+ return !settings.getShowMainToolbar() && !settings.getShowToolbarInNavigationBar();
+ }
+
+ private static final String WIDGET_ID = "settingsEntryPointWidget";
+
+ public static class StatusBarManager implements StatusBarWidgetFactory {
+ @Override
+ public @NonNls @NotNull String getId() {
+ return WIDGET_ID;
+ }
+
+ @Override
+ public @Nls @NotNull String getDisplayName() {
+ return IdeBundle.message("settings.entry.point.tooltip");
+ }
+
+ @Override
+ public boolean isAvailable(@NotNull Project project) {
+ return isAvailableInStatusBar();
+ }
+
+ @Override
+ public @NotNull StatusBarWidget createWidget(@NotNull Project project) {
+ return new MyStatusBarWidget();
+ }
+
+ @Override
+ public void disposeWidget(@NotNull StatusBarWidget widget) {
+ Disposer.dispose(widget);
+ }
+
+ @Override
+ public boolean canBeEnabledOn(@NotNull StatusBar statusBar) {
+ return isAvailableInStatusBar();
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return false;
+ }
+ }
+
+ private static class MyStatusBarWidget implements StatusBarWidget, StatusBarWidget.IconPresentation {
+ private StatusBar myStatusBar;
+ private Icon myIcon;
+
+ private MyStatusBarWidget() {
+ initPluginsListeners();
+ }
+
+ @Override
+ public @NonNls @NotNull String ID() {
+ return WIDGET_ID;
+ }
+
+ @Override
+ public void install(@NotNull StatusBar statusBar) {
+ myStatusBar = statusBar;
+
+ JComponent component = ((IdeStatusBarImpl)statusBar).getWidgetComponent(WIDGET_ID);
+ if (component != null) {
+ createGotItTooltip(this).showAt(Balloon.Position.above, component, c -> new Point(c.getWidth() / 2, 0));
+ }
+ }
+
+ @Override
+ public @Nullable WidgetPresentation getPresentation() {
+ return this;
+ }
+
+ @Override
+ public @Nullable @NlsContexts.Tooltip String getTooltipText() {
+ return getActionTooltip();
+ }
+
+ @Override
+ public @Nullable Consumer getClickConsumer() {
+ return event -> {
+ myIcon = AllIcons.General.GearPlain;
+ myStatusBar.updateWidget(WIDGET_ID);
+
+ Component component = event.getComponent();
+ ListPopup popup = createMainPopup(DataManager.getInstance().getDataContext(component));
+ popup.addListener(new JBPopupListener() {
+ @Override
+ public void beforeShown(@NotNull LightweightWindowEvent event) {
+ Point location = component.getLocationOnScreen();
+ Dimension size = popup.getSize();
+ popup.setLocation(new Point(location.x + component.getWidth() - size.width, location.y - size.height));
+ }
+
+ @Override
+ public void onClosed(@NotNull LightweightWindowEvent event) {
+ myIcon = null;
+ myStatusBar.updateWidget(WIDGET_ID);
+ }
+ });
+ popup.show(component);
+ };
+ }
+
+ @Override
+ public @Nullable Icon getIcon() {
+ return myIcon == null ? getActionIcon() : myIcon;
+ }
+
+ @Override
+ public void dispose() {
+ }
+ }
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ShowPluginsWithSearchOptionAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ShowPluginsWithSearchOptionAction.java
new file mode 100644
index 000000000000..fb789f83733a
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/ide/actions/ShowPluginsWithSearchOptionAction.java
@@ -0,0 +1,26 @@
+// Copyright 2000-2020 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.ide.actions;
+
+import com.intellij.ide.plugins.PluginManagerConfigurable;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.options.ShowSettingsUtil;
+import com.intellij.openapi.project.DumbAwareAction;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Alexander Lobas
+ */
+public class ShowPluginsWithSearchOptionAction extends DumbAwareAction {
+ private final String mySearchOption;
+
+ public ShowPluginsWithSearchOptionAction(@NotNull @Nls String text, @NotNull String searchOption) {
+ super(text);
+ mySearchOption = searchOption;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ ShowSettingsUtil.getInstance().showSettingsDialog(e.getProject(), PluginManagerConfigurable.class, c -> c.enableSearch(mySearchOption));
+ }
+}
\ No newline at end of file
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 0f99ee9b700c..599d6fa642dd 100644
--- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java
+++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurable.java
@@ -132,6 +132,8 @@ public class PluginManagerConfigurable
private Collection myInitUpdates;
+ private String myLaterSearchQuery;
+
public PluginManagerConfigurable(@Nullable Project project) {
myPluginModel = new MyPluginModel(project);
}
@@ -203,7 +205,7 @@ public class PluginManagerConfigurable
if (myInitUpdates != null) {
callback.accept(myInitUpdates.size());
}
- myPluginUpdatesService = PluginUpdatesService.connectConfigurable(callback);
+ myPluginUpdatesService = PluginUpdatesService.connectWithCounter(callback);
myPluginModel.setPluginUpdatesService(myPluginUpdatesService);
boolean selectInstalledTab = !ContainerUtil.isEmpty(myInitUpdates);
@@ -236,6 +238,14 @@ public class PluginManagerConfigurable
myInstalledTab.setSearchQuery("/outdated");
}
+ if (myLaterSearchQuery != null) {
+ Runnable search = enableSearch(myLaterSearchQuery);
+ if (search != null) {
+ ApplicationManager.getApplication().invokeLater(search, ModalityState.any());
+ }
+ myLaterSearchQuery = null;
+ }
+
return myCardPanel;
}
@@ -893,7 +903,7 @@ public class PluginManagerConfigurable
myPluginModel.addEnabledGroup(group);
}
- myPluginUpdatesService.connectInstalled(updates -> {
+ myPluginUpdatesService.calculateUpdates(updates -> {
if (ContainerUtil.isEmpty(updates)) {
clearUpdates(myInstalledPanel);
clearUpdates(myInstalledSearchPanel.getPanel());
@@ -1157,6 +1167,10 @@ public class PluginManagerConfigurable
};
myPluginModel.setCancelInstallCallback(descriptor -> {
+ if (myInstalledSearchPanel == null) {
+ return;
+ }
+
PluginsGroup group = myInstalledSearchPanel.getGroup();
if (group.ui != null && group.ui.findComponent(descriptor) != null) {
@@ -1721,6 +1735,10 @@ public class PluginManagerConfigurable
@Nullable
@Override
public Runnable enableSearch(String option) {
+ if (myTabHeaderComponent == null) {
+ myLaterSearchQuery = option;
+ return null;
+ }
if (StringUtil.isEmpty(option) && (myTabHeaderComponent.getSelectionTab() == MARKETPLACE_TAB || myInstalledSearchPanel.isEmpty())) {
return null;
}
@@ -1770,4 +1788,4 @@ public class PluginManagerConfigurable
});
}
}
-}
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurableTreeRenderer.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurableTreeRenderer.java
index bcdae572d331..ce44010a3ab1 100644
--- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurableTreeRenderer.java
+++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurableTreeRenderer.java
@@ -44,14 +44,12 @@ public class PluginManagerConfigurableTreeRenderer extends AncestorListenerAdapt
@Nullable UnnamedConfigurable configurable,
boolean selected) {
if (myTree == null) {
- myService = PluginUpdatesService.connectTreeRenderer(this);
+ myService = PluginUpdatesService.connectWithCounter(this);
tree.addAncestorListener(this);
myTree = tree;
}
- Icon icon = DynamicBundle.LanguageBundleEP.EP_NAME.hasAnyExtensions() ?
- AllIcons.General.LocalizationSettings :
- null;
+ Icon icon = DynamicBundle.LanguageBundleEP.EP_NAME.hasAnyExtensions() ? AllIcons.General.LocalizationSettings : null;
if (icon == null && myCountValue == null) {
return null;
}
@@ -61,16 +59,11 @@ public class PluginManagerConfigurableTreeRenderer extends AncestorListenerAdapt
myExtraLabel.setIcon(icon);
myExtraLabel.setBackground(myCountLabel.getBackground());
- Component component = icon == null ?
- myCountLabel :
- myCountValue == null ?
- myExtraLabel :
- myPanel;
+ Component component = icon == null ? myCountLabel : myCountValue == null ? myExtraLabel : myPanel;
boolean isPanel = component == myPanel;
return Pair.create(
- component,
- (renderer, bounds, text, right, textBaseline) -> {
+ component, (renderer, bounds, text, right, textBaseline) -> {
Dimension size = renderer.getPreferredSize();
int preferredWidth = size.width;
int preferredHeight = size.height;
@@ -101,9 +94,7 @@ public class PluginManagerConfigurableTreeRenderer extends AncestorListenerAdapt
}
private int getPreferredShift(boolean isPanel) {
- return isPanel ?
- getHGap() + myExtraLabel.getPreferredSize().width / 2 :
- 0;
+ return isPanel ? getHGap() + myExtraLabel.getPreferredSize().width / 2 : 0;
}
private static int getHGap() {
diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginUpdatesService.java b/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginUpdatesService.java
index f2fb995a4098..dbe004340d52 100644
--- a/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginUpdatesService.java
+++ b/platform/platform-impl/src/com/intellij/ide/plugins/newui/PluginUpdatesService.java
@@ -13,7 +13,6 @@ import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import javax.swing.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
@@ -25,124 +24,109 @@ import java.util.function.Consumer;
*/
public class PluginUpdatesService {
private static final List SERVICES = new ArrayList<>();
+ private static final Object ourLock = new Object();
private static Collection myCache;
private static boolean myPrepared;
private static boolean myPreparing;
private static boolean myReset;
- private Consumer super Integer> myTreeCallback;
- private Consumer super Integer> myTabCallback;
- private Consumer super Collection> myInstalledPanelCallback;
- private Consumer super Collection> myUpdatePanelCallback;
+ private Consumer super Integer> myCountCallback;
+ private Consumer super Collection> myUpdateCallback;
@NotNull
- public static PluginUpdatesService connectTreeRenderer(@NotNull Consumer super Integer> callback) {
- checkAccess();
-
+ public static PluginUpdatesService connectWithCounter(@NotNull Consumer super Integer> callback) {
PluginUpdatesService service = new PluginUpdatesService();
- SERVICES.add(service);
- service.myTreeCallback = callback;
+ service.myCountCallback = callback;
- if (myPrepared) {
- callback.accept(getCount());
- }
- else {
- calculateUpdates();
+ synchronized (ourLock) {
+ SERVICES.add(service);
+
+ if (myPrepared) {
+ callback.accept(getCount());
+ return service;
+ }
}
+ calculateUpdates();
return service;
}
@NotNull
- public static PluginUpdatesService connectConfigurable(@NotNull Consumer super Integer> callback) {
- checkAccess();
-
+ public static PluginUpdatesService connectWithUpdates(@NotNull Consumer super Collection> callback) {
PluginUpdatesService service = new PluginUpdatesService();
- SERVICES.add(service);
- service.myTabCallback = callback;
+ service.myUpdateCallback = callback;
- if (myPrepared) {
- callback.accept(getCount());
- }
- else {
- calculateUpdates();
+ synchronized (ourLock) {
+ SERVICES.add(service);
+
+ if (myPrepared) {
+ callback.accept(myCache);
+ }
}
return service;
}
- public void connectInstalled(@NotNull Consumer super Collection> callback) {
- checkAccess();
- myInstalledPanelCallback = callback;
-
- if (myPrepared) {
- callback.accept(myCache);
- }
- else {
- calculateUpdates();
- }
- }
-
public void calculateUpdates(@NotNull Consumer super Collection> callback) {
- checkAccess();
- myUpdatePanelCallback = callback;
+ synchronized (ourLock) {
+ myUpdateCallback = callback;
- if (myPrepared) {
- callback.accept(myCache);
- }
- else {
- calculateUpdates();
+ if (myPrepared) {
+ callback.accept(myCache);
+ return;
+ }
}
+ calculateUpdates();
}
public void finishUpdate(@NotNull IdeaPluginDescriptor descriptor) {
- checkAccess();
-
- if (!myPrepared || myCache == null) {
- return;
- }
-
- for (Iterator I = myCache.iterator(); I.hasNext(); ) {
- IdeaPluginDescriptor downloadedDescriptor = I.next();
-
- if (downloadedDescriptor.equals(descriptor)) {
- I.remove();
-
- Integer countValue = getCount();
- for (PluginUpdatesService service : SERVICES) {
- service.runCountCallbacks(countValue);
- }
-
+ synchronized (ourLock) {
+ if (!myPrepared || myCache == null) {
return;
}
+
+ for (Iterator I = myCache.iterator(); I.hasNext(); ) {
+ IdeaPluginDescriptor downloadedDescriptor = I.next();
+
+ if (downloadedDescriptor.equals(descriptor)) {
+ I.remove();
+
+ Integer countValue = getCount();
+ for (PluginUpdatesService service : SERVICES) {
+ service.runCountCallbacks(countValue);
+ }
+
+ return;
+ }
+ }
}
}
public void finishUpdate() {
- checkAccess();
+ synchronized (ourLock) {
+ if (!myPrepared || myCache == null) {
+ return;
+ }
- if (!myPrepared || myCache == null) {
- return;
- }
-
- Integer countValue = getCount();
- for (PluginUpdatesService service : SERVICES) {
- service.runCountCallbacks(countValue);
+ Integer countValue = getCount();
+ for (PluginUpdatesService service : SERVICES) {
+ service.runCountCallbacks(countValue);
+ }
}
}
public void recalculateUpdates() {
- checkAccess();
+ synchronized (ourLock) {
+ for (PluginUpdatesService service : SERVICES) {
+ service.runAllCallbacks(0);
+ }
- for (PluginUpdatesService service : SERVICES) {
- service.runAllCallbacks(0);
- }
-
- if (myPreparing) {
- resetUpdates();
- }
- else {
- calculateUpdates();
+ if (myPreparing) {
+ resetUpdates();
+ }
+ else {
+ calculateUpdates();
+ }
}
}
@@ -151,28 +135,30 @@ public class PluginUpdatesService {
}
public void dispose() {
- checkAccess();
dispose(this);
}
private static void dispose(@NotNull PluginUpdatesService service) {
- SERVICES.remove(service);
+ synchronized (ourLock) {
+ SERVICES.remove(service);
- if (SERVICES.isEmpty()) {
- myCache = null;
- myPrepared = false;
- myPreparing = false;
+ if (SERVICES.isEmpty()) {
+ myCache = null;
+ myPrepared = false;
+ myPreparing = false;
+ }
}
}
public static boolean isNeedUpdate(@NotNull IdeaPluginDescriptor descriptor) {
- checkAccess();
-
PluginId pluginId = descriptor.getPluginId();
- if (myPrepared && myCache != null) {
- for (IdeaPluginDescriptor downloader : myCache) {
- if (pluginId.equals(downloader.getPluginId())) {
- return true;
+
+ synchronized (ourLock) {
+ if (myPrepared && myCache != null) {
+ for (IdeaPluginDescriptor downloader : myCache) {
+ if (pluginId.equals(downloader.getPluginId())) {
+ return true;
+ }
}
}
}
@@ -182,43 +168,46 @@ public class PluginUpdatesService {
@Nullable
public static Collection getUpdates() {
- checkAccess();
- return !myPrepared || myPreparing || myCache == null ? null : myCache;
+ synchronized (ourLock) {
+ return !myPrepared || myPreparing || myCache == null ? null : myCache;
+ }
}
private static void calculateUpdates() {
- if (myPreparing) {
- return;
+ synchronized (ourLock) {
+ if (myPreparing) {
+ return;
+ }
+ myPreparing = true;
+ myCache = null;
}
- myPreparing = true;
- myCache = null;
ApplicationManager.getApplication().executeOnPooledThread(() -> {
UpdateChecker.CheckPluginsUpdateResult updates = UpdateChecker.checkPluginsUpdate(new EmptyProgressIndicator());
ApplicationManager.getApplication().invokeLater(() -> {
- checkAccess();
+ synchronized (ourLock) {
+ myPreparing = false;
- myPreparing = false;
+ if (myReset) {
+ myReset = false;
+ calculateUpdates();
+ return;
+ }
- if (myReset) {
- myReset = false;
- calculateUpdates();
- return;
- }
+ myPrepared = true;
+ List cache = new ArrayList<>();
+ Collection availableUpdates = updates.getAvailableUpdates();
+ if (availableUpdates != null) {
+ cache.addAll(ContainerUtil.map(availableUpdates, (downloader -> downloader.getDescriptor())));
+ }
+ cache.addAll(ContainerUtil.map(updates.getAvailableDisabledUpdates(), (downloader -> downloader.getDescriptor())));
+ myCache = cache;
- myPrepared = true;
- List cache = new ArrayList<>();
- Collection availableUpdates = updates.getAvailableUpdates();
- if (availableUpdates != null) {
- cache.addAll(ContainerUtil.map(availableUpdates, (downloader -> downloader.getDescriptor())));
- }
- cache.addAll(ContainerUtil.map(updates.getAvailableDisabledUpdates(), (downloader -> downloader.getDescriptor())));
- myCache = cache;
-
- Integer countValue = getCount();
- for (PluginUpdatesService service : SERVICES) {
- service.runAllCallbacks(countValue);
+ Integer countValue = getCount();
+ for (PluginUpdatesService service : SERVICES) {
+ service.runAllCallbacks(countValue);
+ }
}
}, ModalityState.any());
});
@@ -227,20 +216,14 @@ public class PluginUpdatesService {
private void runAllCallbacks(@Nullable Integer countValue) {
runCountCallbacks(countValue);
- if (myInstalledPanelCallback != null) {
- myInstalledPanelCallback.accept(myCache);
- }
- if (myUpdatePanelCallback != null) {
- myUpdatePanelCallback.accept(myCache);
+ if (myUpdateCallback != null) {
+ myUpdateCallback.accept(myCache);
}
}
private void runCountCallbacks(@Nullable Integer countValue) {
- if (myTreeCallback != null) {
- myTreeCallback.accept(countValue);
- }
- if (myTabCallback != null) {
- myTabCallback.accept(countValue);
+ if (myCountCallback != null) {
+ myCountCallback.accept(countValue);
}
}
@@ -248,8 +231,4 @@ public class PluginUpdatesService {
private static Integer getCount() {
return myCache == null ? null : myCache.size();
}
-
- private static void checkAccess() {
- assert SwingUtilities.isEventDispatchThread();
- }
}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/notification/EventLogConsole.java b/platform/platform-impl/src/com/intellij/notification/EventLogConsole.java
index 69d82a24aee1..4edc7b17fc55 100644
--- a/platform/platform-impl/src/com/intellij/notification/EventLogConsole.java
+++ b/platform/platform-impl/src/com/intellij/notification/EventLogConsole.java
@@ -260,7 +260,7 @@ final class EventLogConsole {
final NotificationType type = notification.getType();
TextAttributesKey key = type == NotificationType.ERROR
? ConsoleViewContentType.LOG_ERROR_OUTPUT_KEY
- : type == NotificationType.INFORMATION
+ : type == NotificationType.INFORMATION || type == NotificationType.IDE_UPDATE
? ConsoleViewContentType.NORMAL_OUTPUT_KEY
: ConsoleViewContentType.LOG_WARNING_OUTPUT_KEY;
diff --git a/platform/platform-impl/src/com/intellij/notification/impl/NotificationCollector.java b/platform/platform-impl/src/com/intellij/notification/impl/NotificationCollector.java
index ed962128dd35..d25389ceeb4e 100644
--- a/platform/platform-impl/src/com/intellij/notification/impl/NotificationCollector.java
+++ b/platform/platform-impl/src/com/intellij/notification/impl/NotificationCollector.java
@@ -62,7 +62,7 @@ public final class NotificationCollector {
boolean isExpandable) {
List> data = createNotificationData(notification.getGroupId(), notification.id, notification.displayId);
data.add(DISPLAY_TYPE.with(displayType));
- data.add(SEVERITY.with(notification.getType()));
+ data.add(SEVERITY.with(getType(notification)));
data.add(IS_EXPANDABLE.with(isExpandable));
SHOWN.log(project, data);
}
@@ -71,16 +71,21 @@ public final class NotificationCollector {
@NotNull Notification notification) {
List> data = createNotificationData(notification.getGroupId(), notification.id, notification.displayId);
data.add(DISPLAY_TYPE.with(NotificationDisplayType.TOOL_WINDOW));
- data.add(SEVERITY.with(notification.getType()));
+ data.add(SEVERITY.with(getType(notification)));
SHOWN.log(project, data);
}
public void logNotificationLoggedInEventLog(@NotNull Project project, @NotNull Notification notification) {
List> data = createNotificationData(notification.getGroupId(), notification.id, notification.displayId);
- data.add(SEVERITY.with(notification.getType()));
+ data.add(SEVERITY.with(getType(notification)));
LOGGED.log(project, data);
}
+ @NotNull
+ private static NotificationType getType(@NotNull Notification notification) {
+ return notification.getType() == NotificationType.IDE_UPDATE ? NotificationType.INFORMATION : notification.getType();
+ }
+
public void logNotificationBalloonClosedByUser(@Nullable String notificationId,
@Nullable String notificationDisplayId,
@Nullable String groupId) {
diff --git a/platform/platform-impl/src/com/intellij/notification/impl/widget/IdeNotificationArea.java b/platform/platform-impl/src/com/intellij/notification/impl/widget/IdeNotificationArea.java
index e12a720e35f3..a0d5e37ffc8a 100644
--- a/platform/platform-impl/src/com/intellij/notification/impl/widget/IdeNotificationArea.java
+++ b/platform/platform-impl/src/com/intellij/notification/impl/widget/IdeNotificationArea.java
@@ -121,6 +121,7 @@ public class IdeNotificationArea extends JLabel implements CustomStatusBarWidget
case ERROR:
return forToolWindow ? AllIcons.Toolwindows.ErrorEvents : AllIcons.Ide.Notification.ErrorEvents;
case INFORMATION:
+ case IDE_UPDATE:
return forToolWindow ? AllIcons.Toolwindows.InfoEvents : AllIcons.Ide.Notification.InfoEvents;
}
}
@@ -131,14 +132,15 @@ public class IdeNotificationArea extends JLabel implements CustomStatusBarWidget
public static NotificationType getMaximumType(List extends Notification> notifications) {
NotificationType result = null;
for (Notification notification : notifications) {
- if (NotificationType.ERROR == notification.getType()) {
+ NotificationType type = notification.getType();
+ if (NotificationType.ERROR == type) {
return NotificationType.ERROR;
}
- if (NotificationType.WARNING == notification.getType()) {
+ if (NotificationType.WARNING == type) {
result = NotificationType.WARNING;
}
- else if (result == null && NotificationType.INFORMATION == notification.getType()) {
+ else if (result == null && (NotificationType.INFORMATION == type || NotificationType.IDE_UPDATE == type)) {
result = NotificationType.INFORMATION;
}
}
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
index fb1dba1d3b15..6679012efc7e 100644
--- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateDialog.java
+++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateDialog.java
@@ -59,7 +59,7 @@ public class PluginUpdateDialog extends DialogWrapper {
public PluginUpdateDialog(@Nullable Project project,
@NotNull Collection updatedPlugins,
- @NotNull Collection customRepositoryPlugins) {
+ @Nullable Collection customRepositoryPlugins) {
super(true);
setTitle(IdeBundle.message("dialog.title.plugin.updates"));
@@ -82,7 +82,7 @@ public class PluginUpdateDialog extends DialogWrapper {
@Override
@NotNull
protected Collection getCustomRepoPlugins() {
- return customRepositoryPlugins;
+ return customRepositoryPlugins == null ? super.getCustomRepoPlugins() : customRepositoryPlugins;
}
};
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 71c3b666099a..1635ef35802c 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
@@ -3,6 +3,7 @@ package com.intellij.openapi.updateSettings.impl
import com.google.common.annotations.VisibleForTesting
import com.intellij.ide.IdeBundle
+import com.intellij.ide.actions.SettingsEntryPointAction
import com.intellij.ide.externalComponents.ExternalComponentManager
import com.intellij.ide.plugins.*
import com.intellij.ide.plugins.marketplace.BrokenPluginsService
@@ -29,7 +30,6 @@ import com.intellij.openapi.util.Pair.pair
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame
-import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrameUpdater
import com.intellij.reference.SoftReference
import com.intellij.util.Urls
import com.intellij.util.concurrency.AppExecutorUtil
@@ -120,9 +120,7 @@ object UpdateChecker {
val fromSettings = customSettings != null
ProgressManager.getInstance().run(object : Task.Backgroundable(project, IdeBundle.message("updates.checking.progress"), true) {
- override fun run(indicator: ProgressIndicator) = doUpdateAndShowResult(getProject(), !fromSettings,
- fromSettings || WelcomeFrame.getInstance() != null, true,
- settings, indicator, null)
+ override fun run(indicator: ProgressIndicator) = doUpdateAndShowResult(getProject(), !fromSettings, fromSettings, true, settings, indicator, null)
override fun isConditionalModal(): Boolean = fromSettings
override fun shouldStartInBackground(): Boolean = !fromSettings
@@ -557,6 +555,8 @@ object UpdateChecker {
runnable.invoke()
}
else {
+ SettingsEntryPointAction.newPlatformUpdate(checkForUpdateResult, checkPluginsUpdateResult.incompatiblePlugins)
+
IdeUpdateUsageTriggerCollector.trigger("notification.shown")
val title = IdeBundle.message("updates.new.build.notification.title", ApplicationNamesInfo.getInstance().fullProductName,
newBuild.version)
@@ -583,33 +583,32 @@ object UpdateChecker {
}
// don't show notification if all updated plugins is disabled
else if (updatedPlugins.size != updatedPlugins.count { downloader -> PluginManagerCore.isDisabled(downloader.id) }) {
+ SettingsEntryPointAction.newPluginsUpdate(updatedPlugins, checkPluginsUpdateResult.customRepositoryPlugins)
+
val runnable = { PluginManagerConfigurable.showPluginConfigurable(project, updatedPlugins.map { it.descriptor }) }
- val ideFrame = WelcomeFrame.getInstance()
- if (ideFrame is WelcomeFrameUpdater) {
- ideFrame.showPluginUpdates(runnable)
- }
- else {
- val names = updatedPlugins.joinToString { downloader -> StringUtil.wrapWithDoubleQuote(downloader.pluginName) }
- val title = if (updatedPlugins.size == 1) IdeBundle.message("updates.plugin.ready.short.title.available", names) else IdeBundle.message("updates.plugins.ready.short.title.available")
- val message = if (updatedPlugins.size == 1) "" else names
- showNotification(project, title, message, runnable, { notification ->
- notification.actions[0].templatePresentation.text = IdeBundle.message("plugin.settings.link.title")
- val text = if (updatedPlugins.size == 1) IdeBundle.message("plugins.configurable.update.button") else IdeBundle.message("plugin.manager.update.all")
- notification.actions.add(0, object : NotificationAction(text) {
- override fun actionPerformed(e: AnActionEvent, notification: Notification) {
- notification.expire()
- PluginUpdateDialog.runUpdateAll(updatedPlugins, e.getData(PlatformDataKeys.CONTEXT_COMPONENT) as JComponent?)
- }
- })
- notification.addAction(object : NotificationAction(IdeBundle.message("updates.ignore.updates.link", updatedPlugins.size)) {
- override fun actionPerformed(e: AnActionEvent, notification: Notification) {
- notification.expire()
- PluginUpdateDialog.ignorePlugins(updatedPlugins.map { downloader -> downloader.descriptor })
- }
- })
- }, NotificationUniqueType.PLUGINS, "plugins.update.available")
- }
+ val names = updatedPlugins.joinToString { downloader -> StringUtil.wrapWithDoubleQuote(downloader.pluginName) }
+ val title = if (updatedPlugins.size == 1) IdeBundle.message("updates.plugin.ready.short.title.available", names)
+ else IdeBundle.message("updates.plugins.ready.short.title.available")
+ val message = if (updatedPlugins.size == 1) "" else names
+
+ showNotification(project, title, message, runnable, { notification ->
+ notification.actions[0].templatePresentation.text = IdeBundle.message("plugin.settings.link.title")
+ val text = if (updatedPlugins.size == 1) IdeBundle.message("plugins.configurable.update.button")
+ else IdeBundle.message("plugin.manager.update.all")
+ notification.actions.add(0, object : NotificationAction(text) {
+ override fun actionPerformed(e: AnActionEvent, notification: Notification) {
+ notification.expire()
+ PluginUpdateDialog.runUpdateAll(updatedPlugins, e.getData(PlatformDataKeys.CONTEXT_COMPONENT) as JComponent?)
+ }
+ })
+ notification.addAction(object : NotificationAction(IdeBundle.message("updates.ignore.updates.link", updatedPlugins.size)) {
+ override fun actionPerformed(e: AnActionEvent, notification: Notification) {
+ notification.expire()
+ PluginUpdateDialog.ignorePlugins(updatedPlugins.map { downloader -> downloader.descriptor })
+ }
+ })
+ }, NotificationUniqueType.PLUGINS, "plugins.update.available")
}
}
@@ -647,9 +646,6 @@ object UpdateChecker {
}
private fun canEnableNotifications(): Boolean {
- if (WelcomeFrame.getInstance() is WelcomeFrameUpdater) {
- return true
- }
return NotificationsConfigurationImpl.getInstanceImpl().SHOW_BALLOONS && NotificationsConfigurationImpl.getSettings(
getNotificationGroup().displayId).displayType != NotificationDisplayType.NONE
}
@@ -662,7 +658,8 @@ object UpdateChecker {
notificationType: NotificationUniqueType,
notificationDisplayId: String) {
val content = if (message.isEmpty()) "" else XmlStringUtil.wrapInHtml(message)
- val notification = getNotificationGroup().createNotification(title, content, NotificationType.INFORMATION, null, notificationDisplayId)
+ val type = if (notificationType == NotificationUniqueType.PLATFORM) NotificationType.IDE_UPDATE else NotificationType.INFORMATION
+ val notification = getNotificationGroup().createNotification(title, content, type, null, notificationDisplayId)
notification.collapseActionsDirection = Notification.CollapseActionsDirection.KEEP_LEFTMOST
notification.addAction(object : NotificationAction(IdeBundle.message("updates.notification.update.action")) {
override fun actionPerformed(e: AnActionEvent, notification: Notification) {
diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java
index aa966a1490b8..dd65b0b97d65 100644
--- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java
+++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/UpdateInfoDialog.java
@@ -20,6 +20,7 @@ import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
@@ -48,7 +49,7 @@ import static com.intellij.openapi.util.Pair.pair;
/**
* @author pti
*/
-final class UpdateInfoDialog extends AbstractUpdateDialog {
+public final class UpdateInfoDialog extends AbstractUpdateDialog {
private final UpdateChannel myUpdatedChannel;
private final Collection myUpdatedPlugins;
private final BuildInfo myNewBuild;
@@ -58,7 +59,7 @@ final class UpdateInfoDialog extends AbstractUpdateDialog {
private final File myTestPatch;
private final AbstractAction myWhatsNewAction;
- UpdateInfoDialog(@NotNull UpdateChannel channel,
+ public UpdateInfoDialog(@NotNull UpdateChannel channel,
@NotNull BuildInfo newBuild,
@Nullable UpdateChain patches,
boolean enableLink,
@@ -213,30 +214,46 @@ final class UpdateInfoDialog extends AbstractUpdateDialog {
}
private void downloadPatchAndRestart() {
- boolean updatePlugins = !ContainerUtil.isEmpty(myUpdatedPlugins);
- if (updatePlugins && !new PluginUpdateInfoDialog(myUpdatedPlugins).showAndGet()) {
+ if (!ContainerUtil.isEmpty(myUpdatedPlugins) && !new PluginUpdateInfoDialog(myUpdatedPlugins).showAndGet()) {
return; // update cancelled
}
+ downloadPatchAndRestart(myNewBuild, myUpdatedChannel, myPatches, myTestPatch, myUpdatedPlugins, null);
+ }
+ public static void downloadPatchAndRestart(@NotNull BuildInfo newBuild,
+ @NotNull UpdateChannel updatedChannel,
+ @NotNull UpdateChain patches,
+ @Nullable File testPatch,
+ @Nullable Collection updatedPlugins,
+ @Nullable ActionCallback callback) {
new Task.Backgroundable(null, IdeBundle.message("update.preparing"), true, PerformInBackgroundOption.DEAF) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
String[] command;
try {
- if (myTestPatch != null) {
- command = UpdateInstaller.preparePatchCommand(myTestPatch, indicator);
+ if (testPatch != null) {
+ command = UpdateInstaller.preparePatchCommand(testPatch, indicator);
}
else {
- List files = UpdateInstaller.downloadPatchChain(myPatches.getChain(), indicator);
+ List files = UpdateInstaller.downloadPatchChain(patches.getChain(), indicator);
command = UpdateInstaller.preparePatchCommand(files, indicator);
}
}
- catch (ProcessCanceledException e) { throw e; }
+ catch (ProcessCanceledException e) {
+ if (callback != null) {
+ callback.setRejected();
+ }
+ throw e;
+ }
catch (Exception e) {
Logger.getInstance(UpdateInstaller.class).warn(e);
+ if (callback != null) {
+ callback.setRejected();
+ }
+
String title = IdeBundle.message("updates.notification.title", ApplicationNamesInfo.getInstance().getFullProductName());
- String downloadUrl = UpdateInfoPanel.downloadUrl(myNewBuild, myUpdatedChannel);
+ String downloadUrl = UpdateInfoPanel.downloadUrl(newBuild, updatedChannel);
String message = IdeBundle.message("update.downloading.patch.error", e.getMessage(), downloadUrl);
UpdateChecker.getNotificationGroup().createNotification(
title, message, NotificationType.ERROR, NotificationListener.URL_OPENING_LISTENER, "ide.patch.download.failed").notify(null);
@@ -244,8 +261,12 @@ final class UpdateInfoDialog extends AbstractUpdateDialog {
return;
}
- if (updatePlugins) {
- UpdateInstaller.installPluginUpdates(myUpdatedPlugins, indicator);
+ if (!ContainerUtil.isEmpty(updatedPlugins)) {
+ UpdateInstaller.installPluginUpdates(updatedPlugins, indicator);
+ }
+
+ if (callback != null) {
+ callback.setDone();
}
if (ApplicationManager.getApplication().isRestartCapable()) {
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java
index 5e22bf98ef8b..82b2a0f7f282 100644
--- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java
@@ -37,7 +37,6 @@ import com.intellij.ui.*;
import com.intellij.ui.components.JBList;
import com.intellij.ui.components.JBTextField;
import com.intellij.ui.components.labels.ActionLink;
-import com.intellij.ui.components.labels.LinkLabel;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.ui.mac.TouchbarDataKeys;
import com.intellij.ui.scale.JBUIScale;
@@ -70,7 +69,7 @@ import java.util.List;
/**
* @author Konstantin Bulenkov
*/
-public class FlatWelcomeFrame extends JFrame implements IdeFrame, Disposable, AccessibleContextAccessor, WelcomeFrameUpdater {
+public class FlatWelcomeFrame extends JFrame implements IdeFrame, Disposable, AccessibleContextAccessor {
@SuppressWarnings("StaticNonFinalField")
public static boolean USE_TABBED_WELCOME_SCREEN = Boolean.parseBoolean(SystemProperty.get("use.tabbed.welcome.screen", "true"));
@@ -232,9 +231,8 @@ public class FlatWelcomeFrame extends JFrame implements IdeFrame, Disposable, Ac
return pair.second;
}
- private final class FlatWelcomeScreen extends AbstractWelcomeScreen implements WelcomeFrameUpdater {
+ private final class FlatWelcomeScreen extends AbstractWelcomeScreen {
private final DefaultActionGroup myTouchbarActions = new DefaultActionGroup();
- private LinkLabel