Plugins management 2019

This commit is contained in:
Alexander Lobas
2019-04-19 13:30:19 +03:00
parent 8e5643e21f
commit ff731e45e0
11 changed files with 625 additions and 77 deletions
@@ -3,7 +3,6 @@ package com.intellij.ide.plugins;
import com.intellij.icons.AllIcons;
import com.intellij.ide.plugins.newui.CellPluginComponent;
import com.intellij.ide.plugins.newui.TextHorizontalLayout;
import com.intellij.ui.components.labels.LinkLabel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -15,18 +14,19 @@ import javax.swing.*;
*/
public class LinkPanel {
private final JLabel myTextLabel = new JLabel();
private final JLabel myLinkLabel = createLink();
private final JLabel myLinkLabel;
private Runnable myRunnable;
public LinkPanel(@NotNull JPanel parent) {
public LinkPanel(@NotNull JPanel parent, boolean icon, @Nullable Object labelConstraints, @Nullable Object linkConstraints) {
myLinkLabel = createLink(icon);
myTextLabel.setForeground(CellPluginComponent.GRAY_COLOR);
parent.add(myTextLabel, TextHorizontalLayout.FIX_LABEL);
parent.add(myLinkLabel);
parent.add(myTextLabel, labelConstraints);
parent.add(myLinkLabel, linkConstraints);
}
@NotNull
private JLabel createLink() {
LinkLabel<Object> linkLabel = new LinkLabel<>(null, AllIcons.Ide.External_link_arrow, (__, ___) -> myRunnable.run());
private JLabel createLink(boolean icon) {
LinkLabel<Object> linkLabel = new LinkLabel<>(null, icon ? AllIcons.Ide.External_link_arrow : null, (__, ___) -> myRunnable.run());
linkLabel.setIconTextGap(0);
linkLabel.setHorizontalTextPosition(SwingConstants.LEFT);
return linkLabel;
@@ -2,12 +2,12 @@
package com.intellij.ide.plugins;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.plugins.newui.*;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ex.ApplicationInfoEx;
@@ -17,25 +17,37 @@ import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupListener;
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
import com.intellij.openapi.updateSettings.impl.PluginDownloader;
import com.intellij.openapi.updateSettings.impl.UpdateSettings;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.ThrowableNotNullFunction;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.JBColor;
import com.intellij.ui.SeparatorWithText;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.components.JBTextField;
import com.intellij.ui.components.fields.ExtendableTextComponent;
import com.intellij.ui.components.labels.LinkLabel;
import com.intellij.ui.components.labels.LinkListener;
import com.intellij.ui.popup.PopupFactoryImpl;
import com.intellij.ui.popup.list.PopupListElementRenderer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.ui.JBDimension;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.StatusText;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.io.IOException;
import java.util.List;
@@ -88,7 +100,16 @@ public class PluginManagerConfigurableNewLayout
private Map<String, IdeaPluginDescriptor> myAllRepositoriesMap;
private Map<String, List<IdeaPluginDescriptor>> myCustomRepositoriesMap;
private final Object myRepositoriesLock = new Object();
private List<String> myAllTagSorted;
private List<String> myTagsSorted;
private List<String> myVendorsSorted;
private DefaultActionGroup myMarketplaceSortByGroup;
private Consumer<MarketplaceSortByAction> myMarketplaceSortByCallback;
private LinkComponent myMarketplaceSortByAction;
private DefaultActionGroup myInstalledSearchGroup;
private Runnable myInstalledSearchCallback;
private boolean myInstalledSearchSetState = true;
@NotNull
@Override
@@ -215,6 +236,50 @@ public class PluginManagerConfigurableNewLayout
return actions;
}
private static void showRightBottomPopup(@NotNull Component component, @NotNull String title, @NotNull ActionGroup group) {
DefaultActionGroup actions = new GroupByActionGroup();
actions.addSeparator(" " + title);
actions.addAll(group);
DataContext context = DataManager.getInstance().getDataContext(component);
JBPopup popup = new PopupFactoryImpl.ActionGroupPopup(null, actions, context, false, false, false, true, null, -1, null, null) {
@Override
protected ListCellRenderer getListElementRenderer() {
return new PopupListElementRenderer(this) {
@Override
protected SeparatorWithText createSeparator() {
return new SeparatorWithText() {
{
setTextForeground(JBColor.BLACK);
setFont(UIUtil.getLabelFont());
setCaptionCentered(false);
}
@Override
protected void paintLine(Graphics g, int x, int y, int width) {
}
};
}
@Override
protected Border getDefaultItemComponentBorder() {
return new EmptyBorder(JBUI.insets(UIUtil.getListCellVPadding(), 15));
}
};
}
};
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 + component.getHeight()));
}
});
popup.show(component);
}
private void resetPanels() {
synchronized (myRepositoriesLock) {
myAllRepositoriesList = null;
@@ -222,6 +287,9 @@ public class PluginManagerConfigurableNewLayout
myCustomRepositoriesMap = null;
}
myTagsSorted = null;
myVendorsSorted = null;
myPluginUpdatesService.recalculateUpdates();
if (myMarketplacePanel == null) {
@@ -345,11 +413,12 @@ public class PluginManagerConfigurableNewLayout
@Override
protected List<String> getAttributes() {
List<String> attributes = new ArrayList<>();
attributes.add("tag:");
attributes.add("/tag:");
attributes.add("/sortBy:");
attributes.add("/vendor:");
if (!UpdateSettings.getInstance().getPluginHosts().isEmpty()) {
attributes.add("repository:");
attributes.add("/repository:");
}
attributes.add("sortBy:");
return attributes;
}
@@ -357,8 +426,8 @@ public class PluginManagerConfigurableNewLayout
@Override
protected List<String> getValues(@NotNull String attribute) {
switch (attribute) {
case "tag:":
if (ContainerUtil.isEmpty(myAllTagSorted)) {
case "/tag:":
if (ContainerUtil.isEmpty(myTagsSorted)) {
Set<String> allTags = new HashSet<>();
for (IdeaPluginDescriptor descriptor : getPluginRepositories()) {
if (descriptor instanceof PluginNode) {
@@ -368,13 +437,25 @@ public class PluginManagerConfigurableNewLayout
}
}
}
myAllTagSorted = ContainerUtil.sorted(allTags, String::compareToIgnoreCase);
myTagsSorted = ContainerUtil.sorted(allTags, String::compareToIgnoreCase);
}
return myAllTagSorted;
case "repository:":
return UpdateSettings.getInstance().getPluginHosts();
case "sortBy:":
return myTagsSorted;
case "/sortBy:":
return ContainerUtil.list("downloads", "name", "rating", "featured", "updated");
case "/vendor:":
if (ContainerUtil.isEmpty(myVendorsSorted)) {
Set<String> vendors = new HashSet<>();
for (IdeaPluginDescriptor descriptor : getPluginRepositories()) {
String vendor = StringUtil.trim(descriptor.getVendor());
if (!StringUtil.isEmptyOrSpaces(vendor)) {
vendors.add(vendor);
}
}
myVendorsSorted = ContainerUtil.sorted(vendors, String::compareToIgnoreCase);
}
return myVendorsSorted;
case "/repository:":
return UpdateSettings.getInstance().getPluginHosts();
}
return null;
}
@@ -403,6 +484,98 @@ public class PluginManagerConfigurableNewLayout
}
};
myMarketplaceSortByGroup = new DefaultActionGroup();
for (SortBySearchOption option : SortBySearchOption.values()) {
myMarketplaceSortByGroup.addAction(new MarketplaceSortByAction(option));
}
myMarketplaceSortByAction = new LinkComponent() {
@Override
protected boolean isInClickableArea(Point pt) {
return true;
}
};
myMarketplaceSortByAction.setIcon(new Icon() {
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
getIcon().paintIcon(c, g, x, y + 1);
}
@Override
public int getIconWidth() {
return getIcon().getIconWidth();
}
@Override
public int getIconHeight() {
return getIcon().getIconHeight();
}
@NotNull
private Icon getIcon() {
return AllIcons.General.ButtonDropTriangle;
}
}); // TODO: icon
myMarketplaceSortByAction.setPaintUnderline(false);
myMarketplaceSortByAction.setIconTextGap(JBUI.scale(4));
myMarketplaceSortByAction.setHorizontalTextPosition(SwingConstants.LEFT);
myMarketplaceSortByAction.setForeground(PluginsGroupComponent.SECTION_HEADER_FOREGROUND);
//noinspection unchecked
myMarketplaceSortByAction.setListener(
(component, __) -> showRightBottomPopup(component.getParent().getParent(), "Sort By", myMarketplaceSortByGroup), null);
myMarketplaceSortByCallback = updateAction -> {
MarketplaceSortByAction removeAction = null;
MarketplaceSortByAction addAction = null;
if (updateAction.myState) {
for (AnAction action : myMarketplaceSortByGroup.getChildren(null)) {
MarketplaceSortByAction sortByAction = (MarketplaceSortByAction)action;
if (sortByAction != updateAction && sortByAction.myState) {
sortByAction.myState = false;
removeAction = sortByAction;
break;
}
}
addAction = updateAction;
}
else {
removeAction = updateAction;
}
List<String> queries = new ArrayList<>();
new SearchQueryParser.Marketplace(searchTextField.getText()) {
@Override
protected void setSearchQuery(@NotNull String query) {
super.setSearchQuery(query);
queries.add(query);
}
@Override
protected void handleAttribute(@NotNull String name, @NotNull String value, boolean invert) {
super.handleAttribute(name, value, invert);
queries.add(name + ":" + value);
}
};
if (removeAction != null) {
queries.remove(removeAction.getQuery());
}
if (addAction != null) {
queries.add(addAction.getQuery());
}
String query = StringUtil.join(queries, " ");
searchTextField.setTextIgnoreEvents(query);
if (query.isEmpty()) {
myMarketplaceTab.hideSearchPanel();
}
else {
myMarketplaceTab.showSearchPanel(query);
}
};
PluginsGroupComponentWithProgress panel =
new PluginsGroupComponentWithProgress(new PluginListLayout(), new MultiSelectionEventHandler(), myNameListener,
PluginManagerConfigurableNewLayout.this.mySearchListener,
@@ -420,7 +593,18 @@ public class PluginManagerConfigurableNewLayout
Map<String, IdeaPluginDescriptor> allRepositoriesMap = p.first;
Map<String, List<IdeaPluginDescriptor>> customRepositoriesMap = p.second;
SearchQueryParser.Trending parser = new SearchQueryParser.Trending(query);
SearchQueryParser.Marketplace parser = new SearchQueryParser.Marketplace(query);
// TODO: parser.vendors on server
if (!parser.vendors.isEmpty()) {
for (IdeaPluginDescriptor descriptor : getPluginRepositories()) {
if (parser.vendors.contains(StringUtil.trim(descriptor.getVendor()))) {
result.descriptors.add(descriptor);
}
}
result.sortByName();
return;
}
if (!parser.repositories.isEmpty()) {
for (String repository : parser.repositories) {
@@ -467,6 +651,21 @@ public class PluginManagerConfigurableNewLayout
result.descriptors.addAll(0, builtinList);
}
if (!result.descriptors.isEmpty()) {
String title = "Sort By";
for (AnAction action : myMarketplaceSortByGroup.getChildren(null)) {
MarketplaceSortByAction sortByAction = (MarketplaceSortByAction)action;
sortByAction.setState(parser);
if (sortByAction.myState) {
title = "Sort By: " + sortByAction.myOption.name();
}
}
myMarketplaceSortByAction.setText(title);
result.addRightAction(myMarketplaceSortByAction);
}
}
catch (IOException e) {
PluginManagerMain.LOG.info(e);
@@ -483,7 +682,25 @@ public class PluginManagerConfigurableNewLayout
}
private void createInstalledTab() {
myInstalledSearchGroup = new DefaultActionGroup();
for (InstalledSearchOption option : InstalledSearchOption.values()) {
myInstalledSearchGroup.add(new InstalledSearchOptionAction(option));
}
myInstalledTab = new PluginsTab() {
@Override
protected void createSearchTextField() {
super.createSearchTextField();
JBTextField textField = mySearchTextField.getTextEditor();
textField.putClientProperty("search.extension", ExtendableTextComponent.Extension
.create(AllIcons.Actions.More, AllIcons.Actions.More, "Search Options", // TODO: icon
() -> showRightBottomPopup(textField, "Show", myInstalledSearchGroup)));
textField.putClientProperty("JTextField.variant", null);
textField.putClientProperty("JTextField.variant", "search");
}
@NotNull
@Override
protected PluginDetailsPageComponent createDetailsPanel(@NotNull LinkListener<Object> searchListener) {
@@ -595,6 +812,16 @@ public class PluginManagerConfigurableNewLayout
selectionListener.accept(myInstalledPanel);
}
@Override
public void hideSearchPanel() {
super.hideSearchPanel();
if (myInstalledSearchSetState) {
for (AnAction action : myInstalledSearchGroup.getChildren(null)) {
((InstalledSearchOptionAction)action).setState(null);
}
}
}
@NotNull
@Override
protected SearchResultPanel createSearchPanel(@NotNull Consumer<PluginsGroupComponent> selectionListener,
@@ -603,13 +830,14 @@ public class PluginManagerConfigurableNewLayout
@NotNull
@Override
protected List<String> getAttributes() {
return ContainerUtil.list("#disabled", "#enabled", "#bundled", "#custom", "#inactive", "#invalid", "#outdated", "#uninstalled");
return ContainerUtil
.list("/disabled", "/enabled", "/bundled", "/custom", "/inactive", "/invalid", "/outdated", "/uninstalled", "/vendor:");
}
@Nullable
@Override
protected List<String> getValues(@NotNull String attribute) {
return null;
return "/vendor:".equals(attribute) ? myPluginModel.getVendors() : null;
}
@Override
@@ -636,48 +864,122 @@ public class PluginManagerConfigurableNewLayout
panel.setSelectionListener(selectionListener);
PluginManagerConfigurableNew.registerCopyProvider(panel);
myInstalledSearchCallback = () -> {
StringBuilder queryBuilder = new StringBuilder();
for (AnAction action : myInstalledSearchGroup.getChildren(null)) {
InstalledSearchOptionAction optionAction = (InstalledSearchOptionAction)action;
if (optionAction.myState) {
if (queryBuilder.length() > 0) {
queryBuilder.append(" ");
}
switch (optionAction.myOption) {
case Enabled:
queryBuilder.append("/enabled");
break;
case Disabled:
queryBuilder.append("/disabled");
break;
case Custom:
queryBuilder.append("/custom");
break;
case Bundled:
queryBuilder.append("/bundled");
break;
case Invalid:
queryBuilder.append("/invalid");
break;
case NeedUpdate:
queryBuilder.append("/outdated");
break;
case Deleted:
queryBuilder.append("/uninstalled");
break;
case NeedRestart:
queryBuilder.append("/inactive");
break;
}
}
}
try {
myInstalledSearchSetState = false;
String query = queryBuilder.toString();
searchTextField.setTextIgnoreEvents(query);
if (query.isEmpty()) {
myInstalledTab.hideSearchPanel();
}
else {
myInstalledTab.showSearchPanel(query);
}
}
finally {
myInstalledSearchSetState = true;
}
};
myInstalledSearchPanel = new SearchResultPanel(installedController, panel, 0, 0) {
@Override
protected void handleQuery(@NotNull String query, @NotNull PluginsGroup result) {
InstalledPluginsState state = InstalledPluginsState.getInstance();
SearchQueryParser.Installed parser = new SearchQueryParser.Installed(query);
SearchQueryParser.InstalledWithVendor parser = new SearchQueryParser.InstalledWithVendor(query);
for (UIPluginGroup uiGroup : myInstalledPanel.getGroups()) {
for (CellPluginComponent plugin : uiGroup.plugins) {
if (parser.attributes) {
if (parser.enabled != null && parser.enabled != myPluginModel.isEnabled(plugin.myPlugin)) {
continue;
}
if (parser.bundled != null && parser.bundled != plugin.myPlugin.isBundled()) {
continue;
}
if (parser.invalid != null && parser.invalid != myPluginModel.hasErrors(plugin.myPlugin)) {
continue;
}
if (parser.deleted != null) {
if (plugin.myPlugin instanceof IdeaPluginDescriptorImpl) {
if (parser.deleted != ((IdeaPluginDescriptorImpl)plugin.myPlugin).isDeleted()) {
if (myInstalledSearchSetState) {
for (AnAction action : myInstalledSearchGroup.getChildren(null)) {
((InstalledSearchOptionAction)action).setState(parser);
}
}
if (parser.vendors.isEmpty()) {
for (UIPluginGroup uiGroup : myInstalledPanel.getGroups()) {
for (CellPluginComponent plugin : uiGroup.plugins) {
if (parser.attributes) {
if (parser.enabled != null && parser.enabled != myPluginModel.isEnabled(plugin.myPlugin)) {
continue;
}
if (parser.bundled != null && parser.bundled != plugin.myPlugin.isBundled()) {
continue;
}
if (parser.invalid != null && parser.invalid != myPluginModel.hasErrors(plugin.myPlugin)) {
continue;
}
if (parser.deleted != null) {
if (plugin.myPlugin instanceof IdeaPluginDescriptorImpl) {
if (parser.deleted != ((IdeaPluginDescriptorImpl)plugin.myPlugin).isDeleted()) {
continue;
}
}
else if (parser.deleted) {
continue;
}
}
else if (parser.deleted) {
PluginId pluginId = plugin.myPlugin.getPluginId();
if (parser.needUpdate != null && parser.needUpdate != state.hasNewerVersion(pluginId)) {
continue;
}
if (parser.needRestart != null) {
if (parser.needRestart != (state.wasInstalled(pluginId) || state.wasUpdated(pluginId))) {
continue;
}
}
}
PluginId pluginId = plugin.myPlugin.getPluginId();
if (parser.needUpdate != null && parser.needUpdate != state.hasNewerVersion(pluginId)) {
if (parser.searchQuery != null && !StringUtil.containsIgnoreCase(plugin.myPlugin.getName(), parser.searchQuery)) {
continue;
}
if (parser.needRestart != null) {
if (parser.needRestart != (state.wasInstalled(pluginId) || state.wasUpdated(pluginId))) {
continue;
}
result.descriptors.add(plugin.myPlugin);
}
}
}
else {
for (UIPluginGroup uiGroup : myInstalledPanel.getGroups()) {
for (CellPluginComponent plugin : uiGroup.plugins) {
if (parser.vendors.contains(StringUtil.trim(plugin.myPlugin.getVendor()))) {
result.descriptors.add(plugin.myPlugin);
}
}
if (parser.searchQuery != null && !StringUtil.containsIgnoreCase(plugin.myPlugin.getName(), parser.searchQuery)) {
continue;
}
result.descriptors.add(plugin.myPlugin);
}
}
}
@@ -688,6 +990,136 @@ public class PluginManagerConfigurableNewLayout
};
}
private enum SortBySearchOption {
Downloads, Name, Rating, Relevance, Updated
}
private class MarketplaceSortByAction extends ToggleAction {
private final SortBySearchOption myOption;
private boolean myState;
private MarketplaceSortByAction(@NotNull SortBySearchOption option) {
super(option.name());
myOption = option;
}
@Override
public boolean isSelected(@NotNull AnActionEvent e) {
return myState;
}
@Override
public void setSelected(@NotNull AnActionEvent e, boolean state) {
myState = state;
myMarketplaceSortByCallback.accept(this);
}
public void setState(@NotNull SearchQueryParser.Marketplace parser) {
if (parser.sortBy == null) {
myState = myOption == SortBySearchOption.Relevance;
}
else if (myOption == SortBySearchOption.Relevance) {
myState = "featured".equals(parser.sortBy);
}
else {
myState = myOption.name().equalsIgnoreCase(parser.sortBy);
}
}
@NotNull
public String getQuery() {
switch (myOption) {
case Downloads:
return "/sortBy:downloads";
case Name:
return "/sortBy:name";
case Rating:
return "/sortBy:rating";
case Updated:
return "/sortBy:updated";
case Relevance:
default:
return "/sortBy:featured";
}
}
}
private enum InstalledSearchOption {
Enabled, Disabled, Custom, Bundled, Invalid, NeedUpdate, Deleted, NeedRestart
}
private class InstalledSearchOptionAction extends ToggleAction {
private final InstalledSearchOption myOption;
private boolean myState;
private InstalledSearchOptionAction(@NotNull InstalledSearchOption option) {
myOption = option;
String text;
if (option == InstalledSearchOption.Custom) {
text = "Downloaded";
}
else if (option == InstalledSearchOption.NeedUpdate) {
text = "Update available";
}
else if (option == InstalledSearchOption.NeedRestart) {
text = "Installed or Updated";
}
else {
text = option.name();
}
getTemplatePresentation().setText(text);
}
@Override
public boolean isSelected(@NotNull AnActionEvent e) {
return myState;
}
@Override
public void setSelected(@NotNull AnActionEvent e, boolean state) {
myState = state;
myInstalledSearchCallback.run();
}
public void setState(@Nullable SearchQueryParser.Installed parser) {
if (parser == null) {
myState = false;
return;
}
switch (myOption) {
case Enabled:
myState = parser.enabled != null && parser.enabled;
break;
case Disabled:
myState = parser.enabled != null && !parser.enabled;
break;
case Custom:
myState = parser.bundled != null && !parser.bundled;
break;
case Bundled:
myState = parser.bundled != null && parser.bundled;
break;
case Invalid:
myState = parser.invalid != null && parser.invalid;
break;
case NeedUpdate:
myState = parser.needUpdate != null && parser.needUpdate;
break;
case Deleted:
myState = parser.deleted != null && parser.deleted;
break;
case NeedRestart:
myState = parser.needRestart != null && parser.needRestart;
break;
}
}
}
private static class GroupByActionGroup extends DefaultActionGroup implements CheckedActionGroup {
}
private class ChangePluginStateAction extends AnAction {
private final boolean myEnable;
@@ -1,6 +1,7 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins.newui;
import com.intellij.ide.plugins.PluginManagerConfigurableNew;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.ui.DialogWrapper;
@@ -23,9 +24,9 @@ public class ErrorComponent {
private static final String KEY = "EnableCallback";
@NotNull
public static JComponent create(@NotNull JPanel panel, @Nullable Object constraints) {
public static JComponent create(@NotNull JPanel panel, boolean tinyFont, @Nullable Object constraints) {
JEditorPane editorPane = new JEditorPane();
panel.add(editorPane, constraints);
panel.add(tinyFont ? PluginManagerConfigurableNew.installTiny(editorPane) : editorPane, constraints);
editorPane.setEditable(false);
editorPane.setFocusable(false);
@@ -67,12 +68,13 @@ public class ErrorComponent {
@NotNull
public static JComponent show(@NotNull JPanel panel,
boolean tinyFont,
@Nullable Object constraints,
@Nullable JComponent errorComponent,
@NotNull String message,
@Nullable String action,
@Nullable Runnable enableCallback) {
JComponent component = errorComponent == null ? create(panel, constraints) : errorComponent;
JComponent component = errorComponent == null ? create(panel, tinyFont, constraints) : errorComponent;
show(component, message, action, enableCallback);
return component;
}
@@ -10,6 +10,7 @@ import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.ex.ProgressIndicatorEx;
import com.intellij.openapi.wm.ex.StatusBarEx;
@@ -37,6 +38,7 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM
private PluginsGroup myInstalling;
private PluginsGroup myUpdates;
private Configurable.TopComponentController myTopController;
private List<String> myVendorsSorted;
private static final Set<IdeaPluginDescriptor> myInstallingPlugins = new HashSet<>();
private static final Set<IdeaPluginDescriptor> myInstallingWithUpdatesPlugins = new HashSet<>();
@@ -387,6 +389,8 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM
return;
}
myVendorsSorted = null;
if (myDownloaded.ui == null) {
myDownloaded.descriptors.add(descriptor);
myDownloaded.titleWithEnabled(this);
@@ -412,6 +416,23 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM
}
}
@NotNull
public List<String> getVendors() {
if (ContainerUtil.isEmpty(myVendorsSorted)) {
assert myDownloaded != null;
Set<String> vendors = new HashSet<>();
for (IdeaPluginDescriptor descriptor : myDownloaded.descriptors) {
String vendor = StringUtil.trim(descriptor.getVendor());
if (!StringUtil.isEmptyOrSpaces(vendor)) {
vendors.add(vendor);
}
}
myVendorsSorted = ContainerUtil.sorted(vendors, String::compareToIgnoreCase);
}
return myVendorsSorted;
}
public boolean isEnabled(@NotNull IdeaPluginDescriptor plugin) {
Boolean enabled = getEnabledMap().get(plugin.getPluginId());
return enabled == null || enabled;
@@ -254,8 +254,9 @@ public class NewListPluginComponent extends CellPluginComponent {
Ref<String> enableAction = new Ref<>();
String message = PluginManagerConfigurableNew.getErrorMessage(myPluginModel, myPlugin, enableAction);
myErrorComponent = ErrorComponent.show(myCenterPanel, VerticalLayout.FILL_HORIZONTAL, myErrorComponent, message, enableAction.get(),
enableAction.isNull() ? null : () -> myPluginModel.enableRequiredPlugins(myPlugin));
myErrorComponent = ErrorComponent
.show(myCenterPanel, true, VerticalLayout.FILL_HORIZONTAL, myErrorComponent, message, enableAction.get(),
enableAction.isNull() ? null : () -> myPluginModel.enableRequiredPlugins(myPlugin));
if (addListeners) {
myEventHandler.add(myErrorComponent);
@@ -132,7 +132,7 @@ public class PluginDetailsPageComponent extends MultiPanel {
createButtons();
centerPanel.add(myNameAndButtons, VerticalLayout.FILL_HORIZONTAL);
if (!myMarketplace) {
myErrorComponent = ErrorComponent.create(centerPanel, VerticalLayout.FILL_HORIZONTAL);
myErrorComponent = ErrorComponent.create(centerPanel, false, VerticalLayout.FILL_HORIZONTAL);
}
createMetricsPanel(centerPanel);
@@ -208,9 +208,9 @@ public class PluginDetailsPageComponent extends MultiPanel {
myDownloads =
GridCellPluginComponent.createRatingLabel(panel1, null, "", AllIcons.Plugins.Downloads, CellPluginComponent.GRAY_COLOR, false);
}
myVendor = new LinkPanel(panel1);
myVendor = new LinkPanel(panel1, false, null, TextHorizontalLayout.FIX_LABEL);
JPanel panel2 = new NonOpaquePanel(new TextHorizontalLayout(myMarketplace ? offset : JBUI.scale(7)));
JPanel panel2 = new NonOpaquePanel(new HorizontalLayout(myMarketplace ? offset : JBUI.scale(7)));
if (myMarketplace) {
panel2.add(myTagPanel = new TagPanel(mySearchListener));
}
@@ -251,7 +251,7 @@ public class PluginDetailsPageComponent extends MultiPanel {
bottomPanel.add(mySize = new JLabel());
}
myHomePage = new LinkPanel(bottomPanel);
myHomePage = new LinkPanel(bottomPanel, true, null, null);
}
public void showPlugin(@Nullable CellPluginComponent component) {
@@ -354,7 +354,8 @@ public class PluginDetailsPageComponent extends MultiPanel {
myVendor.hide();
}
else {
myVendor.show(vendor, null); // TODO: link for search
myVendor.show(vendor, () -> mySearchListener
.linkSelected(null, "/vendor:" + (vendor.indexOf(' ') == -1 ? vendor : StringUtil.wrapWithDoubleQuote(vendor))));
}
if (bundled) {
@@ -51,6 +51,11 @@ public class PluginLogo {
});
}
@NotNull
public static Icon getIcon(@NotNull IdeaPluginDescriptor descriptor, int width, int height) {
return null;
}
@NotNull
public static Icon getIcon(@NotNull IdeaPluginDescriptor descriptor, boolean big, boolean jb, boolean error, boolean disabled) {
return getIcon(descriptor).getIcon(big, jb, error, disabled);
@@ -112,7 +112,7 @@ public class PluginsGroupComponent extends JBPanelWithEmptyText {
}
}
private static final Color SECTION_HEADER_FOREGROUND =
public static final Color SECTION_HEADER_FOREGROUND =
JBColor.namedColor("Plugins.SectionHeader.foreground", new JBColor(0x787878, 0x999999));
private static final Color SECTION_HEADER_BACKGROUND =
JBColor.namedColor("Plugins.SectionHeader.background", new JBColor(0xF7F7F7, 0x3C3F41));
@@ -38,7 +38,7 @@ public abstract class PluginsTab {
private PluginDetailsPageComponent myDetailsPage;
private MultiPanel myCardPanel;
private PluginSearchTextField mySearchTextField;
protected PluginSearchTextField mySearchTextField;
private SearchResultPanel mySearchPanel;
public final LinkListener<Object> mySearchListener = (__, data) -> {
@@ -47,7 +47,7 @@ public abstract class PluginsTab {
query = (String)data;
}
else if (data instanceof TagComponent) {
query = SearchQueryParser.getTagQuery(((TagComponent)data).getText());
query = "/" + SearchQueryParser.getTagQuery(((TagComponent)data).getText());
}
else {
return;
@@ -73,13 +73,9 @@ public abstract class PluginsTab {
@Override
public void addNotify() {
super.addNotify();
EventHandler.addGlobalAction(mySearchTextField, new CustomShortcutSet(KeyStroke.getKeyStroke("meta alt F")), () -> {
IdeFocusManager.getGlobalInstance()
.doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(mySearchTextField, true));
if (mySearchPanel.controller != null) {
showSearchPopup();
}
});
EventHandler.addGlobalAction(mySearchTextField, new CustomShortcutSet(KeyStroke.getKeyStroke("meta alt F")),
() -> IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(
() -> IdeFocusManager.getGlobalInstance().requestFocus(mySearchTextField, true)));
}
@Override
@@ -115,7 +111,7 @@ public abstract class PluginsTab {
return splitter;
}
private void createSearchTextField() {
protected void createSearchTextField() {
mySearchTextField = new PluginSearchTextField() {
@Override
protected boolean preprocessEventForTextField(KeyEvent event) {
@@ -239,7 +235,7 @@ public abstract class PluginsTab {
mySearchPanel.setQuery(query);
}
private void hideSearchPanel() {
public void hideSearchPanel() {
if (!mySearchPanel.isEmpty()) {
myCardPanel.select(0, true);
mySearchPanel.setQuery("");
@@ -94,7 +94,7 @@ public abstract class SearchQueryParser {
return;
}
if (size == 1) {
searchQuery = words.get(0);
setSearchQuery(words.get(0));
return;
}
@@ -113,15 +113,19 @@ public abstract class SearchQueryParser {
}
}
else if (searchQuery == null) {
searchQuery = name;
setSearchQuery(name);
}
else {
searchQuery = query;
setSearchQuery(query);
return;
}
}
}
protected void setSearchQuery(@NotNull String query) {
searchQuery = query;
}
protected abstract void handleAttribute(@NotNull String name, @NotNull String value, boolean invert);
@NotNull
@@ -138,6 +142,9 @@ public abstract class SearchQueryParser {
parse(query);
}
protected Trending() {
}
@NotNull
public String getUrlQuery() {
StringBuilder url = new StringBuilder();
@@ -189,6 +196,30 @@ public abstract class SearchQueryParser {
}
}
public static class Marketplace extends Trending {
public final Set<String> vendors = new HashSet<>();
public Marketplace(@NotNull String query) {
parse(query);
}
@Override
protected void handleAttribute(@NotNull String name, @NotNull String value, boolean invert) {
if (name.equals("/vendor")) {
vendors.add(value);
}
else if (name.startsWith("/")) {
super.handleAttribute(name.substring(1), value, invert);
}
}
@NotNull
@Override
public String getUrlQuery() {
return super.getUrlQuery(); // TODO: Auto-generated method stub
}
}
public static class Installed extends SearchQueryParser {
public Boolean enabled; // False == disabled
public Boolean bundled; // False == custom
@@ -196,9 +227,16 @@ public abstract class SearchQueryParser {
public Boolean needUpdate;
public Boolean deleted;
public Boolean needRestart; // inactive & after update
public final boolean attributes;
public boolean attributes;
public Installed(@NotNull String query) {
localParse(query);
}
protected Installed() {
}
private void localParse(@NotNull String query) {
for (String word : splitQuery(query)) {
if (word.startsWith("#")) {
handleAttribute(word.substring(1), "", false);
@@ -214,7 +252,10 @@ public abstract class SearchQueryParser {
break;
}
}
parseEnd();
}
protected void parseEnd() {
attributes = enabled != null || bundled != null || invalid != null || needUpdate != null || deleted != null || needRestart != null;
}
@@ -253,4 +294,49 @@ public abstract class SearchQueryParser {
}
}
}
public static class InstalledWithVendor extends Installed {
public final Set<String> vendors = new HashSet<>();
public InstalledWithVendor(@NotNull String query) {
localParse(query);
}
private void localParse(@NotNull String query) {
List<String> words = splitQuery(query);
int size = words.size();
if (size == 0) {
return;
}
int index = 0;
while (index < size) {
String name = words.get(index++);
if (name.startsWith("/")) {
if (name.equals("/vendor:")) {
if (index < size) {
vendors.add(words.get(index++));
}
else {
searchQuery = query;
break;
}
}
else {
handleAttribute(name.substring(1), "", false);
}
}
else if (searchQuery == null) {
searchQuery = name;
}
else {
searchQuery = query;
break;
}
}
parseEnd();
}
}
}
@@ -535,6 +535,10 @@ public abstract class TextFieldWithPopupHandlerUI extends BasicTextFieldUI imple
}
}
else if ("search".equals(variant)) {
Object extension = getComponent().getClientProperty("search.extension");
if (extension instanceof Extension) {
addExtension((Extension)extension);
}
addExtension(new SearchExtension());
addExtension(new ClearExtension());
}