IDEA-252419 Quick Access Menu to Themes, Plugins, Keymaps and IDE/Plugin updates

GitOrigin-RevId: fc66de1f90999e421870545817cadc33c27214fc
This commit is contained in:
Alexander Lobas
2020-11-02 18:17:34 +00:00
committed by intellij-monorepo-bot
parent 0fafd5eeb7
commit 0c2028d2e5
31 changed files with 852 additions and 282 deletions
@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" rx="8" fill="#EDA200"/>
<rect x="8.80005" y="4.8" width="7.2" height="1.6" transform="rotate(90 8.80005 4.8)" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 4L12 8L4 8L8 4Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 346 B

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" rx="8" fill="#F0A732"/>
<rect x="8.80005" y="4.8" width="7.2" height="1.6" transform="rotate(90 8.80005 4.8)" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 4L12 8L4 8L8 4Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 346 B

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" rx="8" fill="#389FD6"/>
<rect x="8.80005" y="4.8" width="7.2" height="1.6" transform="rotate(90 8.80005 4.8)" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 4L12 8L4 8L8 4Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 346 B

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" rx="8" fill="#3592C4"/>
<rect x="8.80005" y="4.8" width="7.2" height="1.6" transform="rotate(90 8.80005 4.8)" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 4L12 8L4 8L8 4Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 346 B

@@ -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.
@@ -19,6 +19,7 @@ package com.intellij.notification;
* @author spleaner
*/
public enum NotificationType {
IDE_UPDATE,
INFORMATION,
WARNING,
ERROR
@@ -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
@@ -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
@@ -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);
}
}
@@ -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<PluginDownloader> 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<PluginDownloader> 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<IdeaPluginDescriptor> myIncompatiblePlugins;
private static Collection<PluginDownloader> myUpdatedPlugins;
private static Collection<IdeaPluginDescriptor> myCustomRepositoryPlugins;
public static void newPlatformUpdate(@Nullable CheckForUpdateResult platformUpdateInfo,
@Nullable Collection<IdeaPluginDescriptor> incompatiblePlugins) {
myPlatformUpdateInfo = platformUpdateInfo;
myIncompatiblePlugins = incompatiblePlugins;
updateAction();
}
public static void newPluginsUpdate(@Nullable Collection<PluginDownloader> updatedPlugins,
@Nullable Collection<IdeaPluginDescriptor> 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<MouseEvent> 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() {
}
}
}
@@ -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));
}
}
@@ -132,6 +132,8 @@ public class PluginManagerConfigurable
private Collection<IdeaPluginDescriptor> 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
});
}
}
}
}
@@ -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() {
@@ -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<PluginUpdatesService> SERVICES = new ArrayList<>();
private static final Object ourLock = new Object();
private static Collection<IdeaPluginDescriptor> 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<IdeaPluginDescriptor>> myInstalledPanelCallback;
private Consumer<? super Collection<IdeaPluginDescriptor>> myUpdatePanelCallback;
private Consumer<? super Integer> myCountCallback;
private Consumer<? super Collection<IdeaPluginDescriptor>> 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<IdeaPluginDescriptor>> 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<IdeaPluginDescriptor>> callback) {
checkAccess();
myInstalledPanelCallback = callback;
if (myPrepared) {
callback.accept(myCache);
}
else {
calculateUpdates();
}
}
public void calculateUpdates(@NotNull Consumer<? super Collection<IdeaPluginDescriptor>> 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<IdeaPluginDescriptor> 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<IdeaPluginDescriptor> 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<IdeaPluginDescriptor> 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<IdeaPluginDescriptor> cache = new ArrayList<>();
Collection<PluginDownloader> 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<IdeaPluginDescriptor> cache = new ArrayList<>();
Collection<PluginDownloader> 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();
}
}
@@ -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;
@@ -62,7 +62,7 @@ public final class NotificationCollector {
boolean isExpandable) {
List<EventPair<?>> 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<EventPair<?>> 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<EventPair<?>> 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) {
@@ -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;
}
}
@@ -59,7 +59,7 @@ public class PluginUpdateDialog extends DialogWrapper {
public PluginUpdateDialog(@Nullable Project project,
@NotNull Collection<PluginDownloader> updatedPlugins,
@NotNull Collection<IdeaPluginDescriptor> customRepositoryPlugins) {
@Nullable Collection<IdeaPluginDescriptor> customRepositoryPlugins) {
super(true);
setTitle(IdeBundle.message("dialog.title.plugin.updates"));
@@ -82,7 +82,7 @@ public class PluginUpdateDialog extends DialogWrapper {
@Override
@NotNull
protected Collection<IdeaPluginDescriptor> getCustomRepoPlugins() {
return customRepositoryPlugins;
return customRepositoryPlugins == null ? super.getCustomRepoPlugins() : customRepositoryPlugins;
}
};
@@ -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) {
@@ -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<PluginDownloader> 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<PluginDownloader> 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<File> files = UpdateInstaller.downloadPatchChain(myPatches.getChain(), indicator);
List<File> 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()) {
@@ -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<Object> myUpdatePluginsLink;
private boolean inDnd;
FlatWelcomeScreen() {
@@ -352,14 +350,7 @@ public class FlatWelcomeFrame extends JFrame implements IdeFrame, Disposable, Ac
ActionPanel actionPanel = createQuickStartActionPanel();
panel.add(actionPanel, BorderLayout.CENTER);
myTouchbarActions.addAll(actionPanel.getActions());
panel.add(createUpdatesSettingsAndDocs(), BorderLayout.SOUTH);
return panel;
}
private JComponent createUpdatesSettingsAndDocs() {
JPanel panel = new NonOpaquePanel(new BorderLayout());
panel.add(createUpdatePluginsLink(), BorderLayout.WEST);
panel.add(createSettingsAndDocsPanel(FlatWelcomeFrame.this), BorderLayout.EAST);
panel.add(createSettingsAndDocsPanel(FlatWelcomeFrame.this), BorderLayout.SOUTH);
return panel;
}
@@ -469,27 +460,6 @@ public class FlatWelcomeFrame extends JFrame implements IdeFrame, Disposable, Ac
}
return null;
}
private JComponent createUpdatePluginsLink() {
myUpdatePluginsLink = new LinkLabel<>(IdeBundle.message("updates.plugins.welcome.screen.link.message"), null);
myUpdatePluginsLink.setVisible(false);
NonOpaquePanel wrap = new NonOpaquePanel(myUpdatePluginsLink);
wrap.setBorder(JBUI.Borders.empty(0, 10, 8, 11));
return wrap;
}
@Override
public void showPluginUpdates(@NotNull Runnable callback) {
myUpdatePluginsLink.setListener((__, ___) -> callback.run(), null);
myUpdatePluginsLink.setVisible(true);
}
@Override
public void hidePluginUpdates() {
myUpdatePluginsLink.setListener(null, null);
myUpdatePluginsLink.setVisible(false);
}
}
protected void extendActionsGroup(JPanel panel) {
@@ -498,20 +468,6 @@ public class FlatWelcomeFrame extends JFrame implements IdeFrame, Disposable, Ac
protected void onFirstActionShown(@NotNull Component action) {
}
@Override
public void showPluginUpdates(@NotNull Runnable callback) {
if (myScreen instanceof WelcomeFrameUpdater) {
((WelcomeFrameUpdater)myScreen).showPluginUpdates(callback);
}
}
@Override
public void hidePluginUpdates() {
if (myScreen instanceof WelcomeFrameUpdater) {
((WelcomeFrameUpdater)myScreen).hidePluginUpdates();
}
}
@Nullable
@Override
public BalloonLayout getBalloonLayout() {
@@ -2,11 +2,14 @@
package com.intellij.openapi.wm.impl.welcomeScreen;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.plugins.CountComponent;
import com.intellij.ide.plugins.InstalledPluginsState;
import com.intellij.ide.plugins.PluginManagerConfigurable;
import com.intellij.ide.plugins.newui.PluginUpdatesService;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.wm.WelcomeScreenTab;
import com.intellij.openapi.wm.WelcomeTabFactory;
import com.intellij.ui.AncestorListenerAdapter;
@@ -14,11 +17,13 @@ import com.intellij.ui.JBColor;
import com.intellij.util.ui.JBDimension;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UI;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.components.BorderLayoutPanel;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.AncestorEvent;
import java.awt.*;
public class PluginsTabFactory implements WelcomeTabFactory {
private static final Logger LOG = Logger.getInstance(PluginsTabFactory.class);
@@ -26,6 +31,42 @@ public class PluginsTabFactory implements WelcomeTabFactory {
@Override
public @NotNull WelcomeScreenTab createWelcomeTab(@NotNull Disposable parentDisposable) {
return new TabbedWelcomeScreen.DefaultWelcomeScreenTab(IdeBundle.message("welcome.screen.plugins.title"), WelcomeScreenEventCollector.TabType.TabNavPlugins) {
private PluginUpdatesService myService;
private final CountComponent myCountLabel = new CountComponent();
private JList<? extends WelcomeScreenTab> myList;
{
myKeyComponent.setBorder(JBUI.Borders.empty(8, 0, 8, 8));
myKeyComponent.add(myCountLabel, BorderLayout.EAST);
myCountLabel.setVisible(false);
UIUtil.invokeLaterIfNeeded(() -> {
myService = PluginUpdatesService.connectWithCounter(countValue -> {
@NlsSafe String text = countValue == null || countValue <= 0 ? null : countValue.toString();
myCountLabel.setText(text);
myCountLabel.setVisible(text != null);
if (myList != null) {
myList.repaint();
}
});
});
}
@Override
public @NotNull JComponent getKeyComponent(@NotNull JList<? extends WelcomeScreenTab> list) {
if (myList == null) {
list.addAncestorListener(new AncestorListenerAdapter() {
@Override
public void ancestorRemoved(AncestorEvent event) {
if (myService != null) {
myService.dispose();
}
}
});
}
myList = list;
return super.getKeyComponent(list);
}
@Override
protected JComponent buildComponent() {
@@ -131,7 +131,7 @@ public final class TabbedWelcomeScreen extends AbstractWelcomeScreen {
int index,
boolean isSelected,
boolean cellHasFocus) {
JComponent keyComponent = value.getKeyComponent();
JComponent keyComponent = value.getKeyComponent(list);
JPanel wrappedPanel = JBUI.Panels.simplePanel(keyComponent);
UIUtil.setBackgroundRecursively(wrappedPanel, isSelected ? UIUtil.getListSelectionBackground(cellHasFocus): getMainTabListBackground());
UIUtil.setForegroundRecursively(wrappedPanel, UIUtil.getListForeground(isSelected, cellHasFocus));
@@ -143,8 +143,7 @@ public final class TabbedWelcomeScreen extends AbstractWelcomeScreen {
}
public abstract static class DefaultWelcomeScreenTab implements WelcomeScreenTab, Accessible {
private final JComponent myKeyComponent;
protected final JComponent myKeyComponent;
private JComponent myAssociatedComponent;
private final JBLabel myLabel;
private final WelcomeScreenEventCollector.TabType myType;
@@ -162,7 +161,7 @@ public final class TabbedWelcomeScreen extends AbstractWelcomeScreen {
@Override
@NotNull
public JComponent getKeyComponent() {
public JComponent getKeyComponent(@NotNull JList<? extends WelcomeScreenTab> list) {
return myKeyComponent;
}
@@ -1,13 +0,0 @@
// 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.openapi.wm.impl.welcomeScreen;
import org.jetbrains.annotations.NotNull;
/**
* @author Alexander Lobas
*/
public interface WelcomeFrameUpdater {
void showPluginUpdates(@NotNull Runnable callback);
void hidePluginUpdates();
}
@@ -307,7 +307,9 @@ public final class WelcomeScreenComponentFactory {
Topics.subscribe(WelcomeBalloonLayoutImpl.BALLOON_NOTIFICATION_TOPIC, parentDisposable, types -> {
if (!types.isEmpty()) {
NotificationType type = Collections.max(types);
actionLink.setIcon(IdeNotificationArea.createIconWithNotificationCount(panel, type, types.size(), false));
actionLink.setIcon(type == NotificationType.IDE_UPDATE
? AllIcons.Ide.Notification.IdeUpdate
: IdeNotificationArea.createIconWithNotificationCount(panel, type, types.size(), false));
}
panel.setVisible(!types.isEmpty());
});
@@ -1171,6 +1171,7 @@
<statusBarWidgetFactory id="fatalErrorWidget" implementation="com.intellij.openapi.wm.impl.status.FatalErrorWidgetFactory" order="after notificationsWidget"/>
<statusBarWidgetFactory id="writeActionWidget" implementation="com.intellij.openapi.wm.impl.status.WriteThreadIndicatorWidgetFactory" order="after fatalErrorWidget"/>
<statusBarWidgetFactory id="memoryUsageWidget" implementation="com.intellij.openapi.wm.impl.status.MemoryIndicatorWidgetFactory" order="last"/>
<statusBarWidgetFactory id="settingsEntryPointWidget" implementation="com.intellij.ide.actions.SettingsEntryPointAction$StatusBarManager" order="last"/>
<fileIndentOptionsProvider implementation="com.intellij.psi.codeStyle.DetectableIndentOptionsProvider" order="last"/>
<lang.formatter.restriction implementation="com.intellij.formatting.ExcludedFileFormattingRestriction"/>
@@ -501,8 +501,8 @@
<separator/>
<reference id="NavBarToolBarOthers"/>
<separator/>
<reference ref="RunAnything"/>
<reference ref="SearchEverywhere"/>
<reference ref="SettingsEntryPoint"/>
</group>
@@ -165,6 +165,7 @@
</action>
<action id="OpenElementInNewWindow" class="com.intellij.ide.actions.OpenElementInNewWindowAction" />
<action id="SearchEverywhere" class="com.intellij.ide.actions.SearchEverywhereAction" icon="AllIcons.Actions.Find"/>
<action id="SettingsEntryPoint" class="com.intellij.ide.actions.SettingsEntryPointAction" icon="AllIcons.General.GearPlain"/>
<action id="RunAnything" icon="AllIcons.Actions.Run_anything" class="com.intellij.ide.actions.runAnything.RunAnythingAction"/>
@@ -808,12 +809,10 @@
<reference ref="Back"/>
<reference ref="Forward"/>
<separator/>
<group id="MainToolBarSettings">
<reference ref="ShowSettings"/>
</group>
<group id="MainToolBarSettings"/>
<separator/>
<reference ref="RunAnything"/>
<reference ref="SearchEverywhere"/>
<reference ref="SettingsEntryPoint"/>
</group>
<group id="NavBarVcsGroup"/>
@@ -1353,5 +1352,16 @@
</group>
<action class="com.intellij.diagnostic.ResetWindowsDefenderNotification" id="ResetWindowsDefenderNotification"/>
<group id="SettingsEntryPointGroup">
<reference id="RunAnything"/>
<separator/>
<reference id="ShowSettings"/>
<reference id="WelcomeScreen.Plugins"/>
<separator/>
<reference id="ChangeLaf"/>
<reference id="ChangeKeymap"/>
<reference id="ChangeView"/>
</group>
</actions>
</idea-plugin>
@@ -584,8 +584,10 @@ public class AllIcons {
/** 16x16 */ public static final @NotNull Icon ExpandHover = load("ide/notification/expandHover.svg", -6923529455314709892L, 0);
/** 16x16 */ public static final @NotNull Icon Gear = load("ide/notification/gear.svg", -6517441912232259723L, 0);
/** 16x16 */ public static final @NotNull Icon GearHover = load("ide/notification/gearHover.svg", 8529034088585467841L, 0);
/** 16x16 */ public static final @NotNull Icon IdeUpdate = load("ide/notification/ideUpdate.svg", -5687507774382455241L, 2);
/** 16x16 */ public static final @NotNull Icon InfoEvents = load("ide/notification/infoEvents.svg", 8320605274562923160L, 2);
/** 16x16 */ public static final @NotNull Icon NoEvents = load("ide/notification/noEvents.svg", -5934625381114731420L, 2);
/** 16x16 */ public static final @NotNull Icon PluginUpdate = load("ide/notification/pluginUpdate.svg", 7670193732028920558L, 2);
/** 16x16 */ public static final @NotNull Icon WarningEvents = load("ide/notification/warningEvents.svg", -7338122458505861009L, 2);
}
@@ -341,7 +341,7 @@
<reference ref="Vcs.ShowTabbedFileHistory"/>
<reference ref="ChangesView.Revert"/>
<separator/>
<add-to-group group-id="MainToolBarSettings" relative-to-action="ShowSettings" anchor="before"/>
<add-to-group group-id="MainToolBarSettings"/>
</group>
<group id="VcsNavBarToolbarActions">
+1 -2
View File
@@ -37,8 +37,7 @@
<action id="ShowProjectStructureSettings" class="com.intellij.ide.actions.ShowStructureSettingsAction"
icon="AllIcons.General.ProjectStructure">
<add-to-group group-id="FileMainSettingsGroup" anchor="after" relative-to-action="ShowSettings"/>
<add-to-group group-id="MainToolBarSettings" anchor="after" relative-to-action="ShowSettings"/>
<add-to-group group-id="NavBarToolBar" relative-to-action="NavBarToolBarOthers" anchor="after"/>
<add-to-group group-id="SettingsEntryPointGroup" anchor="before" relative-to-action="ShowSettings"/>
</action>
<action id="TemplateProjectStructure" class="com.intellij.ide.actions.TemplateProjectStructureAction">
<add-to-group group-id="FileSettingsGroup" anchor="last"/>