mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[service-view] extract services to separate tab
GitOrigin-RevId: 9d0c0c594cadf8e248bcd87492231d0a29f59247
This commit is contained in:
committed by
intellij-monorepo-bot
parent
3393b98461
commit
cea104758e
@@ -21,6 +21,11 @@ public interface ServiceViewDescriptor {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
default ItemPresentation getContentPresentation() {
|
||||
return getPresentation();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
default ActionGroup getToolbarActions() {
|
||||
return null;
|
||||
|
||||
+18
-1
@@ -3,6 +3,7 @@ package com.intellij.execution.dashboard;
|
||||
|
||||
import com.intellij.execution.ExecutionBundle;
|
||||
import com.intellij.execution.actions.StopAction;
|
||||
import com.intellij.execution.configurations.RunConfiguration;
|
||||
import com.intellij.execution.dashboard.tree.ConfigurationTypeDashboardGroupingRule;
|
||||
import com.intellij.execution.dashboard.tree.RunConfigurationNode;
|
||||
import com.intellij.execution.runners.FakeRerunAction;
|
||||
@@ -148,7 +149,23 @@ public class RunConfigurationsServiceViewContributor
|
||||
@Override
|
||||
public JComponent getContentComponent() {
|
||||
Content content = node.getContent();
|
||||
return content == null ? createEmptyContent() : content.getManager().getComponent();
|
||||
if (content == null) return createEmptyContent();
|
||||
|
||||
ContentManager contentManager = content.getManager();
|
||||
return contentManager == null ? null : contentManager.getComponent();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ItemPresentation getContentPresentation() {
|
||||
Content content = node.getContent();
|
||||
if (content != null) {
|
||||
return new PresentationData(content.getDisplayName(), null, content.getIcon(), null);
|
||||
}
|
||||
else {
|
||||
RunConfiguration configuration = node.getConfigurationSettings().getConfiguration();
|
||||
return new PresentationData(configuration.getName(), null, configuration.getIcon(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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.execution.services;
|
||||
|
||||
import com.intellij.execution.services.ServiceModel.ServiceViewItem;
|
||||
import com.intellij.execution.services.ServiceViewDragHelper.ServiceViewDragBean;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ExtractServiceAction extends DumbAwareAction {
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent e) {
|
||||
List<ServiceViewItem> items = getSelectedItems(e);
|
||||
e.getPresentation().setEnabled(!items.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
if (project == null) return;
|
||||
|
||||
List<ServiceViewItem> items = getSelectedItems(e);
|
||||
if (items.isEmpty()) return;
|
||||
|
||||
((ServiceViewManagerImpl)ServiceViewManager.getInstance(e.getProject())).extract(new ServiceViewDragBean(items));
|
||||
}
|
||||
|
||||
private static List<ServiceViewItem> getSelectedItems(@NotNull AnActionEvent e) {
|
||||
Component contextComponent = e.getData(PlatformDataKeys.CONTEXT_COMPONENT);
|
||||
while (contextComponent != null && !(contextComponent instanceof ServiceView)) {
|
||||
contextComponent = contextComponent.getParent();
|
||||
}
|
||||
return contextComponent == null ? Collections.emptyList() : ((ServiceView)contextComponent).getSelectedItems();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
// 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.execution.services;
|
||||
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.concurrency.Invoker;
|
||||
import com.intellij.util.concurrency.InvokerSupplier;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.concurrency.CancellablePromise;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
class ServiceModel implements Disposable, InvokerSupplier {
|
||||
static final ExtensionPointName<ServiceViewContributor> EP_NAME = ExtensionPointName.create("com.intellij.serviceViewContributor");
|
||||
|
||||
private final Project myProject;
|
||||
private final Invoker myInvoker = new Invoker.BackgroundThread(this);
|
||||
private final List<ServiceViewItem> myRoots = new ArrayList<>();
|
||||
private boolean myRootInitialized;
|
||||
|
||||
ServiceModel(@NotNull Project project) {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Invoker getInvoker() {
|
||||
return myInvoker;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
List<? extends ServiceViewItem> getRoots() {
|
||||
if (!myRootInitialized) {
|
||||
myRootInitialized = true;
|
||||
myRoots.clear();
|
||||
myRoots.addAll(doGetRoots());
|
||||
}
|
||||
return myRoots;
|
||||
}
|
||||
|
||||
private List<? extends ServiceViewItem> doGetRoots() {
|
||||
List<ServiceViewItem> result = new ArrayList<>();
|
||||
for (ServiceViewContributor<?> contributor : EP_NAME.getExtensions()) {
|
||||
result.addAll(getContributorChildren(myProject, null, contributor));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
CancellablePromise<?> refresh(@NotNull ServiceEventListener.ServiceEvent e) {
|
||||
return getInvoker().runOrInvokeLater(() -> reset(e.contributorClass));
|
||||
}
|
||||
|
||||
private void reset(Class<?> contributorClass) {
|
||||
int startIndex = -1;
|
||||
|
||||
if (myRoots.isEmpty()) {
|
||||
startIndex = 0;
|
||||
}
|
||||
else {
|
||||
Map<ServiceViewContributor, Integer> indexes = new HashMap<>();
|
||||
List<ServiceViewItem> toRemove = new ArrayList<>();
|
||||
ServiceViewContributor previous = null;
|
||||
for (int i = 0; i < myRoots.size(); i++) {
|
||||
ServiceViewItem child = myRoots.get(i);
|
||||
if (contributorClass.isInstance(child.getContributor())) {
|
||||
toRemove.add(child);
|
||||
if (startIndex < 0) {
|
||||
startIndex = i;
|
||||
}
|
||||
}
|
||||
else if (previous != child.getContributor()) {
|
||||
previous = child.getContributor();
|
||||
indexes.put(previous, i);
|
||||
}
|
||||
}
|
||||
if (startIndex < 0) {
|
||||
ServiceViewContributor[] contributors = EP_NAME.getExtensions();
|
||||
for (int i = contributors.length - 1; i >= 0; i--) {
|
||||
if (!contributorClass.isInstance(contributors[i])) {
|
||||
startIndex = indexes.getOrDefault(contributors[i], Integer.valueOf(-1));
|
||||
if (startIndex == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (startIndex < 0) {
|
||||
startIndex = myRoots.size() - toRemove.size();
|
||||
}
|
||||
}
|
||||
myRoots.removeAll(toRemove);
|
||||
}
|
||||
|
||||
List<ServiceViewItem> newChildren = null;
|
||||
for (ServiceViewContributor<?> contributor : EP_NAME.getExtensions()) {
|
||||
if (contributorClass.isInstance(contributor)) {
|
||||
newChildren = getContributorChildren(myProject, null, contributor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (newChildren != null) {
|
||||
myRoots.addAll(startIndex, newChildren);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> List<ServiceViewItem> getContributorChildren(Project project,
|
||||
ServiceViewItem parent,
|
||||
ServiceViewContributor<T> contributor) {
|
||||
Set<ServiceViewItem> children = new LinkedHashSet<>();
|
||||
Map<Object, ServiceGroupNode> groupNodes = new HashMap<>();
|
||||
for (T service : contributor.getServices(project)) {
|
||||
Object value = service instanceof ServiceViewProvidingContributor ? ((ServiceViewProvidingContributor)service).asService() : service;
|
||||
|
||||
if (contributor instanceof ServiceViewGroupingContributor) {
|
||||
ServiceViewGroupingContributor<T, Object> groupingContributor = (ServiceViewGroupingContributor<T, Object>)contributor;
|
||||
Object group = groupingContributor.groupBy(service);
|
||||
if (group != null) {
|
||||
ServiceGroupNode groupNode = groupNodes.get(group);
|
||||
if (groupNode == null) {
|
||||
groupNode = new ServiceGroupNode(group, parent, contributor, groupingContributor.getGroupDescriptor(group));
|
||||
groupNodes.put(group, groupNode);
|
||||
}
|
||||
ServiceViewItem
|
||||
serviceNode = new ServiceNode(value, groupNode, contributor, contributor.getServiceDescriptor(service), project,
|
||||
service instanceof ServiceViewContributor ? (ServiceViewContributor)service : null);
|
||||
groupNode.getChildren().add(serviceNode);
|
||||
children.add(groupNode);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ServiceViewItem
|
||||
serviceNode = new ServiceNode(value, parent, contributor, contributor.getServiceDescriptor(service), project,
|
||||
service instanceof ServiceViewContributor ? (ServiceViewContributor)service : null);
|
||||
children.add(serviceNode);
|
||||
}
|
||||
return new ArrayList<>(children);
|
||||
}
|
||||
|
||||
abstract static class ServiceViewItem {
|
||||
private final Object myValue;
|
||||
private final ServiceViewItem myParent;
|
||||
private final ServiceViewContributor myContributor;
|
||||
private final ServiceViewDescriptor myViewDescriptor;
|
||||
private List<ServiceViewItem> myChildren;
|
||||
private boolean myPresentationUpdated;
|
||||
|
||||
protected ServiceViewItem(@NotNull Object value, @Nullable ServiceViewItem parent, @NotNull ServiceViewContributor contributor,
|
||||
@NotNull ServiceViewDescriptor viewDescriptor) {
|
||||
myValue = value;
|
||||
myParent = parent;
|
||||
myContributor = contributor;
|
||||
myViewDescriptor = viewDescriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
Object getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
ServiceViewContributor getContributor() {
|
||||
return myContributor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
ServiceViewContributor getRootContributor() {
|
||||
return myParent == null ? myContributor : myParent.getRootContributor();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
ServiceViewDescriptor getViewDescriptor() {
|
||||
if (!myPresentationUpdated) {
|
||||
myPresentationUpdated = true;
|
||||
if (myValue instanceof NodeDescriptor) {
|
||||
((NodeDescriptor)myValue).update();
|
||||
}
|
||||
}
|
||||
return myViewDescriptor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ServiceViewItem getParent() {
|
||||
return myParent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
List<ServiceViewItem> getChildren() {
|
||||
if (myChildren == null) {
|
||||
myChildren = doGetChildren();
|
||||
}
|
||||
return myChildren;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
abstract List<ServiceViewItem> doGetChildren();
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ServiceViewItem node = (ServiceViewItem)o;
|
||||
return myValue.equals(node.myValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myValue.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
static class ServiceNode extends ServiceViewItem {
|
||||
private final Project myProject;
|
||||
private final ServiceViewContributor<?> myProvidingContributor;
|
||||
|
||||
ServiceNode(@NotNull Object service, @Nullable ServiceViewItem parent, @NotNull ServiceViewContributor contributor,
|
||||
@NotNull ServiceViewDescriptor viewDescriptor,
|
||||
@NotNull Project project, @Nullable ServiceViewContributor providingContributor) {
|
||||
super(service, parent, contributor, viewDescriptor);
|
||||
myProject = project;
|
||||
myProvidingContributor = providingContributor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
List<ServiceViewItem> doGetChildren() {
|
||||
return myProvidingContributor == null ? Collections.emptyList() : getContributorChildren(myProject, this, myProvidingContributor);
|
||||
}
|
||||
}
|
||||
|
||||
static class ServiceGroupNode extends ServiceViewItem {
|
||||
ServiceGroupNode(@NotNull Object group, @Nullable ServiceViewItem parent, @NotNull ServiceViewContributor contributor,
|
||||
@NotNull ServiceViewDescriptor viewDescriptor) {
|
||||
super(group, parent, contributor, viewDescriptor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
List<ServiceViewItem> doGetChildren() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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.execution.services;
|
||||
|
||||
import com.intellij.execution.services.ServiceModel.ServiceViewItem;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
class ServiceModelFilter {
|
||||
private final List<ServiceViewFilter> myFilters = new CopyOnWriteArrayList<>();
|
||||
|
||||
void addFilter(@NotNull ServiceViewFilter filter) {
|
||||
myFilters.add(filter);
|
||||
}
|
||||
|
||||
void removeFilter(@NotNull ServiceViewFilter filter) {
|
||||
ServiceViewFilter parent = filter.getParent();
|
||||
myFilters.remove(filter);
|
||||
for (ServiceViewFilter viewFilter : myFilters) {
|
||||
if (viewFilter.getParent() == filter) {
|
||||
viewFilter.setParent(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
List<? extends ServiceViewItem> filter(@NotNull List<? extends ServiceViewItem> items, @Nullable ServiceViewFilter targetFilter) {
|
||||
if (items.isEmpty()) return items;
|
||||
|
||||
List<ServiceViewFilter> filters = targetFilter == null ? myFilters : excludeTargetAndParents(targetFilter);
|
||||
return ContainerUtil.filter(items, item -> filters.stream().noneMatch(filter -> filter.value(item)));
|
||||
}
|
||||
|
||||
private List<ServiceViewFilter> excludeTargetAndParents(@NotNull ServiceViewFilter targetFilter) {
|
||||
List<ServiceViewFilter> filters = new ArrayList<>(myFilters);
|
||||
do {
|
||||
filters.remove(targetFilter);
|
||||
targetFilter = targetFilter.getParent();
|
||||
}
|
||||
while (targetFilter != null);
|
||||
return filters;
|
||||
}
|
||||
|
||||
abstract static class ServiceViewFilter implements Condition<ServiceViewItem> {
|
||||
private ServiceViewFilter myParent;
|
||||
|
||||
protected ServiceViewFilter(@Nullable ServiceViewFilter parent) {
|
||||
myParent = parent;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ServiceViewFilter getParent() {
|
||||
return myParent;
|
||||
}
|
||||
|
||||
private void setParent(@Nullable ServiceViewFilter parent) {
|
||||
myParent = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
// 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.execution.services;
|
||||
|
||||
import com.intellij.execution.services.ServiceModel.ServiceViewItem;
|
||||
import com.intellij.execution.services.ServiceViewModel.ServiceViewModelListener;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.ui.AppUIUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.concurrency.Promise;
|
||||
import org.jetbrains.concurrency.Promises;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
class ServiceSingleView extends ServiceView {
|
||||
private final AtomicReference<ServiceViewItem> myRef = new AtomicReference<>();
|
||||
private boolean mySelected;
|
||||
private final ServiceViewModelListener myListener;
|
||||
|
||||
ServiceSingleView(@NotNull Project project, @NotNull ServiceViewModel model, @NotNull ServiceViewUi ui) {
|
||||
super(new BorderLayout(), project, model, ui);
|
||||
ui.setServiceToolbar(ServiceViewActionProvider.getInstance());
|
||||
add(ui.getComponent(), BorderLayout.CENTER);
|
||||
myListener = new ServiceViewModelListener() {
|
||||
@Override
|
||||
public void rootsChanged() {
|
||||
updateItem();
|
||||
}
|
||||
};
|
||||
model.addModelListener(myListener);
|
||||
model.getInvoker().invokeLater(this::updateItem);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
Promise<Void> select(@NotNull Object service,
|
||||
@NotNull Class<?> contributorClass) {
|
||||
ServiceViewItem item = myRef.get();
|
||||
if (item == null || !item.getValue().equals(service)) {
|
||||
return Promises.rejectedPromise("Service not found");
|
||||
}
|
||||
|
||||
showContent();
|
||||
return Promises.resolvedPromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveState(@NotNull ServiceViewState state) {
|
||||
}
|
||||
|
||||
@Override
|
||||
void onViewSelected() {
|
||||
showContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
void onViewUnselected() {
|
||||
mySelected = false;
|
||||
ServiceViewItem item = myRef.get();
|
||||
if (item != null) {
|
||||
item.getViewDescriptor().onNodeUnselected();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
List<ServiceViewItem> getSelectedItems() {
|
||||
ServiceViewItem item = myRef.get();
|
||||
return item == null ? Collections.emptyList() : Collections.singletonList(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
getModel().removeModelListener(myListener);
|
||||
}
|
||||
|
||||
private void updateItem() {
|
||||
ServiceViewItem oldValue = myRef.get();
|
||||
ServiceViewItem newValue = ContainerUtil.getOnlyItem(getModel().getRoots());
|
||||
myRef.set(newValue);
|
||||
AppUIUtil.invokeOnEdt(() -> {
|
||||
if (mySelected) {
|
||||
if (newValue != null) {
|
||||
ServiceViewDescriptor descriptor = newValue.getViewDescriptor();
|
||||
if (oldValue == null) {
|
||||
descriptor.onNodeSelected();
|
||||
}
|
||||
myUi.setDetailsComponent(descriptor.getContentComponent());
|
||||
}
|
||||
}
|
||||
}, myProject.getDisposed());
|
||||
}
|
||||
|
||||
private void showContent() {
|
||||
if (mySelected) return;
|
||||
|
||||
mySelected = true;
|
||||
ServiceViewItem item = myRef.get();
|
||||
if (item != null) {
|
||||
ServiceViewDescriptor descriptor = item.getViewDescriptor();
|
||||
descriptor.onNodeSelected();
|
||||
|
||||
myUi.setDetailsComponent(descriptor.getContentComponent());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
// 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.execution.services;
|
||||
|
||||
import com.intellij.execution.services.ServiceModel.ServiceViewItem;
|
||||
import com.intellij.ide.dnd.DnDManager;
|
||||
import com.intellij.ide.util.treeView.TreeState;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
@@ -18,25 +20,24 @@ import javax.swing.tree.TreePath;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.*;
|
||||
|
||||
class ServiceTreeView extends ServiceView {
|
||||
private final Project myProject;
|
||||
private final ServiceViewTree myTree;
|
||||
private final ServiceViewTreeModel myTreeModel;
|
||||
private final ServiceViewUi myUi;
|
||||
|
||||
private ServiceViewItem myLastSelection;
|
||||
private boolean mySelected;
|
||||
|
||||
ServiceTreeView(@NotNull Project project, @NotNull ServiceViewModel model, @NotNull ServiceViewUi ui, @NotNull ServiceViewState state) {
|
||||
super(new BorderLayout());
|
||||
myProject = project;
|
||||
myUi = ui;
|
||||
super(new BorderLayout(), project, model, ui);
|
||||
|
||||
myTreeModel = new ServiceViewTreeModel(model);
|
||||
myTree = new ServiceViewTree(myTreeModel, this);
|
||||
ServiceViewActionProvider actionProvider = ServiceViewActionProvider.getInstance();
|
||||
ui.setServiceToolbar(actionProvider);
|
||||
ui.setMasterPanel(myTree, actionProvider);
|
||||
DnDManager.getInstance().registerSource(ServiceViewDragHelper.createSource(this), myTree);
|
||||
add(myUi.getComponent(), BorderLayout.CENTER);
|
||||
|
||||
myTree.addTreeSelectionListener(e -> onSelectionChanged());
|
||||
@@ -53,7 +54,19 @@ class ServiceTreeView extends ServiceView {
|
||||
@NotNull
|
||||
@Override
|
||||
List<ServiceViewItem> getSelectedItems() {
|
||||
return ContainerUtil.mapNotNull(TreeUtil.collectSelectedUserObjects(myTree), o -> ObjectUtils.tryCast(o, ServiceViewItem.class));
|
||||
int[] rows = myTree.getSelectionRows();
|
||||
if (rows == null || rows.length == 0) return Collections.emptyList();
|
||||
|
||||
List<ServiceViewItem> result = new ArrayList<>();
|
||||
Arrays.sort(rows);
|
||||
for (int row : rows) {
|
||||
TreePath path = myTree.getPathForRow(row);
|
||||
ServiceViewItem item = path == null ? null : ObjectUtils.tryCast(path.getLastPathComponent(), ServiceViewItem.class);
|
||||
if (item != null) {
|
||||
result.add(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,13 +83,31 @@ class ServiceTreeView extends ServiceView {
|
||||
return Promises.resolvedPromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
void onViewSelected() {
|
||||
mySelected = true;
|
||||
if (myLastSelection != null) {
|
||||
ServiceViewDescriptor descriptor = myLastSelection.getViewDescriptor();
|
||||
descriptor.onNodeSelected();
|
||||
myUi.setDetailsComponent(descriptor.getContentComponent());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void onViewUnselected() {
|
||||
mySelected = false;
|
||||
if (myLastSelection != null) {
|
||||
myLastSelection.getViewDescriptor().onNodeUnselected();
|
||||
}
|
||||
}
|
||||
|
||||
private void onSelectionChanged() {
|
||||
List<ServiceViewItem> selected = getSelectedItems();
|
||||
ServiceViewItem newSelection = ContainerUtil.getOnlyItem(selected);
|
||||
if (Comparing.equal(newSelection, myLastSelection)) return;
|
||||
|
||||
ServiceViewDescriptor oldDescriptor = myLastSelection == null ? null : myLastSelection.getViewDescriptor();
|
||||
if (oldDescriptor != null) {
|
||||
if (oldDescriptor != null && mySelected) {
|
||||
oldDescriptor.onNodeUnselected();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.execution.services;
|
||||
|
||||
import com.intellij.execution.services.ServiceModel.ServiceViewItem;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.DataProvider;
|
||||
@@ -16,14 +17,25 @@ import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
abstract class ServiceView extends JPanel implements Disposable {
|
||||
ServiceView(LayoutManager layout) {
|
||||
protected final Project myProject;
|
||||
private final ServiceViewModel myModel;
|
||||
protected final ServiceViewUi myUi;
|
||||
|
||||
ServiceView(LayoutManager layout, @NotNull Project project, @NotNull ServiceViewModel model, @NotNull ServiceViewUi ui) {
|
||||
super(layout);
|
||||
myProject = project;
|
||||
myModel = model;
|
||||
myUi = ui;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
ServiceViewModel getModel() {
|
||||
return myModel;
|
||||
}
|
||||
|
||||
abstract void saveState(@NotNull ServiceViewState state);
|
||||
|
||||
@NotNull
|
||||
@@ -31,13 +43,24 @@ abstract class ServiceView extends JPanel implements Disposable {
|
||||
|
||||
abstract Promise<Void> select(@NotNull Object service, @NotNull Class<?> contributorClass);
|
||||
|
||||
abstract void onViewSelected();
|
||||
|
||||
abstract void onViewUnselected();
|
||||
|
||||
static ServiceView createTreeView(@NotNull Project project, @NotNull ServiceViewModel model, @NotNull ServiceViewState state) {
|
||||
ServiceViewTreeUi ui = new ServiceViewTreeUi(state);
|
||||
ServiceViewUi ui = new ServiceViewTreeUi(state);
|
||||
ServiceView serviceView = new ServiceTreeView(project, model, ui, state);
|
||||
setDataProvider(serviceView);
|
||||
return serviceView;
|
||||
}
|
||||
|
||||
static ServiceView createSingleView(@NotNull Project project, @NotNull ServiceViewModel model) {
|
||||
ServiceViewUi ui = new ServiceViewSingleUi();
|
||||
ServiceView serviceView = new ServiceSingleView(project, model, ui);
|
||||
setDataProvider(serviceView);
|
||||
return serviceView;
|
||||
}
|
||||
|
||||
private static void setDataProvider(ServiceView serviceView) {
|
||||
serviceView.putClientProperty(DataManager.CLIENT_PROPERTY_DATA_PROVIDER, (DataProvider)dataId -> {
|
||||
if (PlatformDataKeys.HELP_ID.is(dataId)) {
|
||||
|
||||
@@ -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.execution.services;
|
||||
|
||||
import com.intellij.execution.services.ServiceModel.ServiceViewItem;
|
||||
import com.intellij.ide.CommonActionsManager;
|
||||
import com.intellij.ide.DefaultTreeExpander;
|
||||
import com.intellij.ide.TreeExpander;
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
// 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.execution.services;
|
||||
|
||||
import com.intellij.execution.services.ServiceModel.ServiceViewItem;
|
||||
import com.intellij.ide.dnd.DnDAction;
|
||||
import com.intellij.ide.dnd.DnDDragStartBean;
|
||||
import com.intellij.ide.dnd.DnDSource;
|
||||
import com.intellij.ide.projectView.PresentationData;
|
||||
import com.intellij.ide.util.treeView.PresentableNodeDescriptor;
|
||||
import com.intellij.navigation.ItemPresentation;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.SimpleColoredComponent;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.List;
|
||||
|
||||
class ServiceViewDragHelper {
|
||||
static DnDSource createSource(ServiceView serviceView) {
|
||||
return new ServiceViewDnDSource(serviceView);
|
||||
}
|
||||
|
||||
static String getDisplayName(ItemPresentation presentation) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (presentation instanceof PresentationData) {
|
||||
List<PresentableNodeDescriptor.ColoredFragment> fragments = ((PresentationData)presentation).getColoredText();
|
||||
if (fragments.isEmpty() && presentation.getPresentableText() != null) {
|
||||
result.append(presentation.getPresentableText());
|
||||
}
|
||||
else {
|
||||
for (PresentableNodeDescriptor.ColoredFragment fragment : fragments) {
|
||||
result.append(fragment.getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (presentation.getPresentableText() != null) {
|
||||
result.append(presentation.getPresentableText());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ServiceViewContributor getTheOnlyContributor(List<ServiceViewItem> nodes) {
|
||||
ServiceViewContributor result = null;
|
||||
for (ServiceViewItem node : nodes) {
|
||||
if (result == null) {
|
||||
result = node.getContributor();
|
||||
}
|
||||
else if (result != node.getContributor()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static class ServiceViewDragBean {
|
||||
private final List<ServiceViewItem> myItems;
|
||||
private final ServiceViewContributor myContributor;
|
||||
|
||||
ServiceViewDragBean(List<ServiceViewItem> items) {
|
||||
myItems = ContainerUtil.filter(items, item -> {
|
||||
ServiceViewItem parent = item.getParent();
|
||||
while (parent != null) {
|
||||
if (items.contains(parent)) {
|
||||
return false;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
myContributor = getTheOnlyContributor(myItems);
|
||||
}
|
||||
|
||||
List<ServiceViewItem> getItems() {
|
||||
return myItems;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ServiceViewContributor getContributor() {
|
||||
return myContributor;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ServiceViewDnDSource implements DnDSource {
|
||||
private final ServiceView myServiceView;
|
||||
|
||||
ServiceViewDnDSource(@NotNull ServiceView serviceView) {
|
||||
myServiceView = serviceView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canStartDragging(DnDAction action, Point dragOrigin) {
|
||||
return !myServiceView.getSelectedItems().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnDDragStartBean startDragging(DnDAction action, Point dragOrigin) {
|
||||
return new DnDDragStartBean(new ServiceViewDragBean(myServiceView.getSelectedItems()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragDropEnd() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropActionChanged(int gestureModifiers) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Image, Point> createDraggedImage(DnDAction action,
|
||||
Point dragOrigin,
|
||||
@NotNull DnDDragStartBean bean) {
|
||||
ServiceViewDragBean dragBean = (ServiceViewDragBean)bean.getAttachedObject();
|
||||
int size = dragBean.getItems().size();
|
||||
ItemPresentation presentation = null;
|
||||
if (size == 1) {
|
||||
presentation = dragBean.getItems().get(0).getViewDescriptor().getPresentation();
|
||||
}
|
||||
else {
|
||||
ServiceViewContributor contributor = dragBean.getContributor();
|
||||
if (contributor != null) {
|
||||
presentation = contributor.getViewDescriptor().getPresentation();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleColoredComponent c = new SimpleColoredComponent();
|
||||
c.setForeground(myServiceView.getForeground());
|
||||
c.setBackground(myServiceView.getBackground());
|
||||
if (presentation != null) {
|
||||
c.setIcon(presentation.getIcon(false));
|
||||
c.append(getDisplayName(presentation));
|
||||
}
|
||||
else {
|
||||
String text = size + StringUtil.pluralize(" item", size);
|
||||
c.append(text);
|
||||
}
|
||||
|
||||
Dimension preferredSize = c.getPreferredSize();
|
||||
c.setSize(preferredSize);
|
||||
BufferedImage image = UIUtil.createImage(c, preferredSize.width, preferredSize.height, BufferedImage.TYPE_INT_ARGB);
|
||||
c.setOpaque(false);
|
||||
Graphics2D g = image.createGraphics();
|
||||
c.paint(g);
|
||||
g.dispose();
|
||||
return Pair.create(image, new Point(0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// 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.execution.services;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
interface ServiceViewItem {
|
||||
@NotNull
|
||||
Object getValue();
|
||||
|
||||
@NotNull
|
||||
ServiceViewDescriptor getViewDescriptor();
|
||||
}
|
||||
+343
-36
@@ -1,9 +1,18 @@
|
||||
// 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.execution.services;
|
||||
|
||||
import com.intellij.execution.services.ServiceModel.ServiceGroupNode;
|
||||
import com.intellij.execution.services.ServiceModel.ServiceViewItem;
|
||||
import com.intellij.execution.services.ServiceViewDragHelper.ServiceViewDragBean;
|
||||
import com.intellij.execution.services.ServiceViewModel.*;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.dnd.DnDDropHandler;
|
||||
import com.intellij.ide.dnd.DnDEvent;
|
||||
import com.intellij.ide.dnd.DnDSupport;
|
||||
import com.intellij.ide.dnd.DnDTargetChecker;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.ide.util.treeView.TreeState;
|
||||
import com.intellij.navigation.ItemPresentation;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
@@ -12,22 +21,23 @@ import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.components.StoragePathMacros;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.wm.ToolWindow;
|
||||
import com.intellij.openapi.wm.ToolWindowAnchor;
|
||||
import com.intellij.openapi.wm.ToolWindowId;
|
||||
import com.intellij.openapi.wm.ToolWindowManager;
|
||||
import com.intellij.openapi.wm.ex.ToolWindowEx;
|
||||
import com.intellij.openapi.wm.impl.InternalDecorator;
|
||||
import com.intellij.ui.AppUIUtil;
|
||||
import com.intellij.ui.AutoScrollFromSourceHandler;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.ui.content.ContentFactory;
|
||||
import com.intellij.ui.content.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.xmlb.annotations.Property;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -36,22 +46,30 @@ import org.jetbrains.concurrency.AsyncPromise;
|
||||
import org.jetbrains.concurrency.Promise;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@State(name = "ServiceViewManager", storages = @Storage(StoragePathMacros.WORKSPACE_FILE))
|
||||
public final class ServiceViewManagerImpl implements ServiceViewManager, PersistentStateComponent<ServiceViewManagerImpl.State> {
|
||||
private static final String AUTO_SCROLL_TO_SOURCE_PROPERTY = "service.view.auto.scroll.to.source";
|
||||
private static final String AUTO_SCROLL_FROM_SOURCE_PROPERTY = "service.view.auto.scroll.from.source";
|
||||
|
||||
static final ExtensionPointName<ServiceViewContributor> EP_NAME =
|
||||
ExtensionPointName.create("com.intellij.serviceViewContributor");
|
||||
|
||||
// TODO [konstantin.aleev] provide help id
|
||||
@NonNls private static final String HELP_ID = "run-dashboard.reference";
|
||||
|
||||
@NotNull private final Project myProject;
|
||||
private final Project myProject;
|
||||
private State myState = new State();
|
||||
|
||||
@NotNull private State myState = new State();
|
||||
private ServiceView myServiceView;
|
||||
private ServiceModel myModel;
|
||||
private ServiceModelFilter myModelFilter;
|
||||
private final List<ServiceView> myServiceViews = ContainerUtil.newSmartList();
|
||||
|
||||
private ContentManager myContentManager;
|
||||
private final Content myDropTargetContent = createDropTargetContent();
|
||||
|
||||
public ServiceViewManagerImpl(@NotNull Project project) {
|
||||
myProject = project;
|
||||
@@ -59,31 +77,101 @@ public final class ServiceViewManagerImpl implements ServiceViewManager, Persist
|
||||
}
|
||||
|
||||
boolean hasServices() {
|
||||
for (ServiceViewContributor<?> contributor : EP_NAME.getExtensions()) {
|
||||
for (ServiceViewContributor<?> contributor : ServiceModel.EP_NAME.getExtensions()) {
|
||||
if (!contributor.getServices(myProject).isEmpty()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void createToolWindowContent(@NotNull ToolWindow toolWindow) {
|
||||
ServiceViewModel model = new ServiceViewModel.AllServicesModel(myProject);
|
||||
myProject.getMessageBus().connect(model).subscribe(ServiceEventListener.TOPIC, model::refresh);
|
||||
myServiceView = ServiceView.createTreeView(myProject, model, myState.viewState);
|
||||
myModel = new ServiceModel(myProject);
|
||||
myModelFilter = new ServiceModelFilter();
|
||||
myProject.getMessageBus().connect(myModel).subscribe(ServiceEventListener.TOPIC, e -> myModel.refresh(e).onSuccess(o -> {
|
||||
for (ServiceView serviceView : myServiceViews) {
|
||||
serviceView.getModel().eventProcessed(e);
|
||||
}
|
||||
}));
|
||||
ServiceView serviceView = ServiceView.createTreeView(myProject, new AllServicesModel(myModel, myModelFilter), myState.viewState);
|
||||
myServiceViews.add(serviceView);
|
||||
|
||||
Content toolWindowContent = ContentFactory.SERVICE.getInstance().createContent(myServiceView, null, false);
|
||||
toolWindowContent.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
|
||||
toolWindowContent.setHelpId(getToolWindowContextHelpId());
|
||||
toolWindowContent.setCloseable(false);
|
||||
Content allServicesContent = ContentFactory.SERVICE.getInstance().createContent(serviceView, null, false);
|
||||
allServicesContent.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
|
||||
allServicesContent.setHelpId(getToolWindowContextHelpId());
|
||||
allServicesContent.setCloseable(false);
|
||||
|
||||
Disposer.register(toolWindowContent, myServiceView);
|
||||
Disposer.register(toolWindowContent, model);
|
||||
Disposer.register(toolWindowContent, () -> myServiceView = null);
|
||||
Disposer.register(allServicesContent, myModel);
|
||||
Disposer.register(allServicesContent, serviceView);
|
||||
Disposer.register(allServicesContent, serviceView.getModel());
|
||||
Disposer.register(allServicesContent, () -> {
|
||||
myServiceViews.clear();
|
||||
myContentManager = null;
|
||||
myModel = null;
|
||||
myModelFilter = null;
|
||||
});
|
||||
|
||||
toolWindow.getContentManager().addContent(toolWindowContent);
|
||||
myContentManager = toolWindow.getContentManager();
|
||||
myContentManager.addContent(allServicesContent);
|
||||
myContentManager.addContentManagerListener(new MyContentMangerListener(allServicesContent));
|
||||
|
||||
ToolWindowEx toolWindowEx = (ToolWindowEx)toolWindow;
|
||||
toolWindowEx.setAdditionalGearActions(new DefaultActionGroup(ToggleAutoScrollAction.toSource(), ToggleAutoScrollAction.fromSource()));
|
||||
toolWindowEx.setTitleActions(ServiceViewAutoScrollFromSourceHandler.install(myProject, toolWindow));
|
||||
installDnDSupport(toolWindowEx.getDecorator());
|
||||
}
|
||||
|
||||
private void installDnDSupport(InternalDecorator decorator) {
|
||||
DnDSupport.createBuilder(decorator)
|
||||
.setTargetChecker(new DnDTargetChecker() {
|
||||
@Override
|
||||
public boolean update(DnDEvent event) {
|
||||
Object o = event.getAttachedObject();
|
||||
boolean dropPossible = o instanceof ServiceViewDragBean && event.getPoint().y < decorator.getHeaderHeight();
|
||||
event.setDropPossible(dropPossible, "");
|
||||
if (dropPossible) {
|
||||
if (myContentManager.getIndexOfContent(myDropTargetContent) < 0) {
|
||||
myContentManager.addContent(myDropTargetContent);
|
||||
}
|
||||
|
||||
ServiceViewDragBean dragBean = (ServiceViewDragBean)o;
|
||||
ItemPresentation presentation;
|
||||
if (dragBean.getItems().size() > 1 && dragBean.getContributor() != null) {
|
||||
presentation = dragBean.getContributor().getViewDescriptor().getPresentation();
|
||||
}
|
||||
else {
|
||||
presentation = dragBean.getItems().get(0).getViewDescriptor().getPresentation();
|
||||
}
|
||||
myDropTargetContent.setDisplayName(ServiceViewDragHelper.getDisplayName(presentation));
|
||||
myDropTargetContent.setIcon(presentation.getIcon(false));
|
||||
}
|
||||
else if (myContentManager.getIndexOfContent(myDropTargetContent) >= 0) {
|
||||
myContentManager.removeContent(myDropTargetContent, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.setCleanUpOnLeaveCallback(() -> {
|
||||
if (myContentManager.getIndexOfContent(myDropTargetContent) >= 0) {
|
||||
myContentManager.removeContent(myDropTargetContent, false);
|
||||
}
|
||||
})
|
||||
.setDropHandler(new DnDDropHandler() {
|
||||
@Override
|
||||
public void drop(DnDEvent event) {
|
||||
Object o = event.getAttachedObject();
|
||||
if (o instanceof ServiceViewDragBean) {
|
||||
extract((ServiceViewDragBean)o);
|
||||
}
|
||||
}
|
||||
})
|
||||
.install();
|
||||
decorator.addMouseMotionListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
if (myContentManager.getIndexOfContent(myDropTargetContent) >= 0) {
|
||||
myContentManager.removeContent(myDropTargetContent, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -92,13 +180,16 @@ public final class ServiceViewManagerImpl implements ServiceViewManager, Persist
|
||||
AsyncPromise<Void> result = new AsyncPromise<>();
|
||||
AppUIUtil.invokeLaterIfProjectAlive(myProject, () -> {
|
||||
Runnable runnable = () -> {
|
||||
ServiceView view = myServiceView;
|
||||
if (view == null) {
|
||||
ContentManager contentManager = myContentManager;
|
||||
List<Content> contents =
|
||||
contentManager == null ? Collections.emptyList() : ContainerUtil.newSmartList(contentManager.getContents());
|
||||
if (contents.isEmpty()) {
|
||||
result.setError("Content not initialized");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
view.select(service, contributorClass).processed(result);
|
||||
}
|
||||
|
||||
Collections.reverse(contents);
|
||||
select(myProject, contents.iterator(), result, service, contributorClass);
|
||||
};
|
||||
ToolWindow window = activate ? ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.SERVICES) : null;
|
||||
if (window != null) {
|
||||
@@ -111,6 +202,32 @@ public final class ServiceViewManagerImpl implements ServiceViewManager, Persist
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void select(Project project, Iterator<Content> iterator, AsyncPromise<Void> result,
|
||||
@NotNull Object service, @NotNull Class<?> contributorClass) {
|
||||
Content content = iterator.next();
|
||||
((ServiceView)content.getComponent()).select(service, contributorClass)
|
||||
.onSuccess(v -> {
|
||||
AppUIUtil.invokeOnEdt(() -> {
|
||||
ContentManager contentManager = content.getManager();
|
||||
if (contentManager == null) return;
|
||||
|
||||
if (contentManager.getSelectedContent() != content && contentManager.getIndexOfContent(content) >= 0) {
|
||||
contentManager.setSelectedContent(content);
|
||||
}
|
||||
}, project.getDisposed());
|
||||
|
||||
result.setResult(null);
|
||||
})
|
||||
.onError(e -> {
|
||||
if (iterator.hasNext()) {
|
||||
select(project, iterator, result, service, contributorClass);
|
||||
}
|
||||
else {
|
||||
result.setError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateToolWindow(ServiceEventListener.ServiceEvent event) {
|
||||
ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
|
||||
if (toolWindowManager == null) return;
|
||||
@@ -148,10 +265,147 @@ public final class ServiceViewManagerImpl implements ServiceViewManager, Persist
|
||||
return toolWindow;
|
||||
}
|
||||
|
||||
void extract(@NotNull ServiceViewDragBean dragBean) {
|
||||
List<ServiceViewItem> items = dragBean.getItems();
|
||||
if (items.isEmpty()) return;
|
||||
|
||||
ServiceViewContributor contributor = dragBean.getContributor();
|
||||
if (contributor != null) {
|
||||
List<? extends ServiceViewItem> roots = myModel.getRoots();
|
||||
List<? extends ServiceViewItem> contributorRoots = ContainerUtil.filter(roots, node -> contributor == node.getContributor());
|
||||
if (contributorRoots.equals(items)) {
|
||||
extractContributor(contributor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (items.size() == 1) {
|
||||
ServiceViewItem item = items.get(0);
|
||||
if (item instanceof ServiceGroupNode) {
|
||||
extractGroup((ServiceGroupNode)item);
|
||||
}
|
||||
else {
|
||||
extractService(item);
|
||||
}
|
||||
return;
|
||||
}
|
||||
extractList(items, -1);
|
||||
}
|
||||
|
||||
private void extractContributor(ServiceViewContributor contributor) {
|
||||
ServiceView serviceView =
|
||||
ServiceView.createTreeView(myProject,
|
||||
new ContributorModel(myModel, myModelFilter, contributor, getSelectedView().getModel().getFilter()),
|
||||
myState.viewState);
|
||||
ItemPresentation presentation = contributor.getViewDescriptor().getContentPresentation();
|
||||
addServiceView(serviceView, ServiceViewDragHelper.getDisplayName(presentation), presentation.getIcon(false));
|
||||
}
|
||||
|
||||
private void extractGroup(ServiceGroupNode group) {
|
||||
AtomicReference<ServiceGroupNode> ref = new AtomicReference<>(group);
|
||||
ServiceView serviceView =
|
||||
ServiceView.createTreeView(myProject,
|
||||
new GroupModel(myModel, myModelFilter, ref, getSelectedView().getModel().getFilter()),
|
||||
myState.viewState);
|
||||
|
||||
ItemPresentation presentation = group.getViewDescriptor().getContentPresentation();
|
||||
Content content = addServiceView(serviceView, ServiceViewDragHelper.getDisplayName(presentation), presentation.getIcon(false));
|
||||
serviceView.getModel().addModelListener(() -> updateContentTab(ref.get(), content));
|
||||
}
|
||||
|
||||
private void extractService(ServiceViewItem service) {
|
||||
AtomicReference<ServiceViewItem> ref = new AtomicReference<>(service);
|
||||
ServiceView serviceView =
|
||||
ServiceView.createSingleView(myProject,
|
||||
new SingeServiceModel(myModel, myModelFilter, ref, getSelectedView().getModel().getFilter()));
|
||||
|
||||
ItemPresentation presentation = service.getViewDescriptor().getContentPresentation();
|
||||
Content content = addServiceView(serviceView, ServiceViewDragHelper.getDisplayName(presentation), presentation.getIcon(false));
|
||||
serviceView.getModel().addModelListener(() -> {
|
||||
ServiceViewItem item = ref.get();
|
||||
if (item != null && !serviceView.getModel().getChildren(item).isEmpty()) {
|
||||
AppUIUtil.invokeOnEdt(() -> {
|
||||
int index = myContentManager.getIndexOfContent(content);
|
||||
myContentManager.removeContent(content, true);
|
||||
extractList(ContainerUtil.newSmartList(item), index);
|
||||
}, myProject.getDisposed());
|
||||
}
|
||||
else {
|
||||
updateContentTab(item, content);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void extractList(List<ServiceViewItem> items, int index) {
|
||||
String name;
|
||||
Icon icon;
|
||||
if (items.size() == 1) {
|
||||
ItemPresentation presentation = items.get(0).getViewDescriptor().getContentPresentation();
|
||||
name = ServiceViewDragHelper.getDisplayName(presentation);
|
||||
icon = presentation.getIcon(false);
|
||||
}
|
||||
else {
|
||||
name = Messages.showInputDialog(myProject, "Group Name:", "Group Services", null, null, null);
|
||||
if (name == null) return;
|
||||
|
||||
icon = AllIcons.Nodes.Folder;
|
||||
}
|
||||
|
||||
ServiceView serviceView =
|
||||
ServiceView.createTreeView(myProject,
|
||||
new ServiceListModel(myModel, myModelFilter, items, getSelectedView().getModel().getFilter()),
|
||||
myState.viewState);
|
||||
Content content = addServiceView(serviceView, name, icon, index);
|
||||
serviceView.getModel().addModelListener(() -> updateContentTab(ContainerUtil.getOnlyItem(serviceView.getModel().getRoots()), content));
|
||||
}
|
||||
|
||||
private Content addServiceView(ServiceView serviceView, String displayName, Icon icon) {
|
||||
return addServiceView(serviceView, displayName, icon, -1);
|
||||
}
|
||||
|
||||
private Content addServiceView(ServiceView serviceView, String displayName, Icon icon, int index) {
|
||||
myServiceViews.add(index == -1 ? myServiceViews.size() : index, serviceView);
|
||||
myModelFilter.addFilter(serviceView.getModel().getFilter());
|
||||
|
||||
Content content = ContentFactory.SERVICE.getInstance().createContent(serviceView, displayName, false);
|
||||
content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
|
||||
content.setHelpId(getToolWindowContextHelpId());
|
||||
content.setCloseable(true);
|
||||
content.setIcon(icon);
|
||||
|
||||
Disposer.register(content, serviceView);
|
||||
Disposer.register(content, serviceView.getModel());
|
||||
|
||||
ServiceView selectedView = getSelectedView();
|
||||
myContentManager.addContent(content, index);
|
||||
myContentManager.setSelectedContent(content);
|
||||
myModel.getInvoker().invokeLater(() -> selectedView.getModel().filtersChanged());
|
||||
serviceView.getModel().addModelListener(() -> {
|
||||
if (serviceView.getModel().getRoots().isEmpty()) {
|
||||
AppUIUtil.invokeOnEdt(() -> myContentManager.removeContent(content, true), myProject.getDisposed());
|
||||
}
|
||||
});
|
||||
return content;
|
||||
}
|
||||
|
||||
private ServiceView getSelectedView() {
|
||||
return (ServiceView)myContentManager.getSelectedContent().getComponent();
|
||||
}
|
||||
|
||||
private void updateContentTab(ServiceViewItem item, Content content) {
|
||||
if (item != null) {
|
||||
ItemPresentation itemPresentation = item.getViewDescriptor().getContentPresentation();
|
||||
AppUIUtil.invokeOnEdt(() -> {
|
||||
content.setDisplayName(ServiceViewDragHelper.getDisplayName(itemPresentation));
|
||||
content.setIcon(itemPresentation.getIcon(false));
|
||||
}, myProject.getDisposed());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public State getState() {
|
||||
ServiceView serviceView = myServiceView;
|
||||
ServiceView serviceView = ContainerUtil.getFirstItem(myServiceViews);
|
||||
if (serviceView != null) {
|
||||
serviceView.saveState(myState.viewState);
|
||||
myState.viewState.treeStateElement = new Element("root");
|
||||
@@ -171,14 +425,6 @@ public final class ServiceViewManagerImpl implements ServiceViewManager, Persist
|
||||
public ServiceViewState viewState = new ServiceViewState();
|
||||
}
|
||||
|
||||
static boolean isAutoScrollToSourceEnabled(@NotNull Project project) {
|
||||
return PropertiesComponent.getInstance(project).getBoolean(AUTO_SCROLL_TO_SOURCE_PROPERTY);
|
||||
}
|
||||
|
||||
private static boolean isAutoScrollFromSourceEnabled(@NotNull Project project) {
|
||||
return PropertiesComponent.getInstance(project).getBoolean(AUTO_SCROLL_FROM_SOURCE_PROPERTY);
|
||||
}
|
||||
|
||||
private static String getToolWindowId() {
|
||||
return ToolWindowId.SERVICES;
|
||||
}
|
||||
@@ -191,6 +437,67 @@ public final class ServiceViewManagerImpl implements ServiceViewManager, Persist
|
||||
return HELP_ID;
|
||||
}
|
||||
|
||||
private static Content createDropTargetContent() {
|
||||
Content content = ContentFactory.SERVICE.getInstance().createContent(new JPanel(), null, false);
|
||||
content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
|
||||
content.setCloseable(true);
|
||||
return content;
|
||||
}
|
||||
|
||||
private class MyContentMangerListener extends ContentManagerAdapter {
|
||||
private final Content myAllServiceContent;
|
||||
|
||||
private MyContentMangerListener(Content allServiceContent) {
|
||||
myAllServiceContent = allServiceContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contentAdded(@NotNull ContentManagerEvent event) {
|
||||
if (myContentManager.getContentCount() > 1) {
|
||||
myAllServiceContent.setDisplayName("All Services");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contentRemoved(@NotNull ContentManagerEvent event) {
|
||||
if (event.getContent() != myDropTargetContent) {
|
||||
ServiceView removedView = (ServiceView)event.getContent().getComponent();
|
||||
myModelFilter.removeFilter(removedView.getModel().getFilter());
|
||||
myServiceViews.remove(removedView);
|
||||
myModel.getInvoker().invokeLater(() -> {
|
||||
for (ServiceView serviceView : myServiceViews) {
|
||||
serviceView.getModel().filtersChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (myContentManager.getContentCount() == 1) {
|
||||
myAllServiceContent.setDisplayName(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectionChanged(@NotNull ContentManagerEvent event) {
|
||||
if (event.getContent() == myDropTargetContent) return;
|
||||
|
||||
ServiceView serviceView = (ServiceView)event.getContent().getComponent();
|
||||
if (event.getOperation() == ContentManagerEvent.ContentOperation.add) {
|
||||
serviceView.onViewSelected();
|
||||
}
|
||||
else {
|
||||
serviceView.onViewUnselected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static boolean isAutoScrollToSourceEnabled(@NotNull Project project) {
|
||||
return PropertiesComponent.getInstance(project).getBoolean(AUTO_SCROLL_TO_SOURCE_PROPERTY);
|
||||
}
|
||||
|
||||
private static boolean isAutoScrollFromSourceEnabled(@NotNull Project project) {
|
||||
return PropertiesComponent.getInstance(project).getBoolean(AUTO_SCROLL_FROM_SOURCE_PROPERTY);
|
||||
}
|
||||
|
||||
private static class ToggleAutoScrollAction extends ToggleAction implements DumbAware {
|
||||
private final String myProperty;
|
||||
|
||||
@@ -243,7 +550,7 @@ public final class ServiceViewManagerImpl implements ServiceViewManager, Persist
|
||||
}
|
||||
|
||||
private boolean select(@NotNull FileEditor editor) {
|
||||
for (ServiceViewContributor extension : EP_NAME.getExtensions()) {
|
||||
for (ServiceViewContributor extension : ServiceModel.EP_NAME.getExtensions()) {
|
||||
if (!(extension instanceof ServiceViewFileEditorContributor)) continue;
|
||||
if (selectContributorNode(editor, (ServiceViewFileEditorContributor)extension, extension.getClass())) return true;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// 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.execution.services;
|
||||
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor;
|
||||
import com.intellij.execution.services.ServiceEventListener.ServiceEvent;
|
||||
import com.intellij.execution.services.ServiceModel.ServiceGroupNode;
|
||||
import com.intellij.execution.services.ServiceModel.ServiceViewItem;
|
||||
import com.intellij.execution.services.ServiceModelFilter.ServiceViewFilter;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.concurrency.Invoker;
|
||||
import com.intellij.util.concurrency.InvokerSupplier;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -11,264 +13,260 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
interface ServiceViewModel extends Disposable, InvokerSupplier {
|
||||
List<? extends ServiceTreeNode> getRoots();
|
||||
abstract class ServiceViewModel implements Disposable, InvokerSupplier {
|
||||
protected final ServiceModel myModel;
|
||||
protected final ServiceModelFilter myModelFilter;
|
||||
private final ServiceViewFilter myFilter;
|
||||
private final List<ServiceViewModelListener> myListeners = ContainerUtil.newSmartList();
|
||||
|
||||
void refresh(@NotNull ServiceEventListener.ServiceEvent e);
|
||||
protected ServiceViewModel(@NotNull ServiceModel model, @NotNull ServiceModelFilter modelFilter, ServiceViewFilter condition) {
|
||||
myModel = model;
|
||||
myModelFilter = modelFilter;
|
||||
myFilter = condition;
|
||||
}
|
||||
|
||||
void addModelListener(ServiceViewModelListener listener);
|
||||
@NotNull
|
||||
List<? extends ServiceViewItem> getRoots() {
|
||||
return filterEmptyGroups(doGetRoots());
|
||||
}
|
||||
|
||||
void removeModelListener(ServiceViewModelListener listener);
|
||||
@NotNull
|
||||
protected abstract List<? extends ServiceViewItem> doGetRoots();
|
||||
|
||||
abstract void eventProcessed(ServiceEvent e);
|
||||
|
||||
void filtersChanged() {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
ServiceViewFilter getFilter() {
|
||||
return myFilter;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
List<? extends ServiceViewItem> getChildren(@NotNull ServiceViewItem parent) {
|
||||
return filterEmptyGroups(myModelFilter.filter(parent.getChildren(), myFilter));
|
||||
}
|
||||
|
||||
void addModelListener(@NotNull ServiceViewModelListener listener) {
|
||||
myListeners.add(listener);
|
||||
}
|
||||
|
||||
void removeModelListener(@NotNull ServiceViewModelListener listener) {
|
||||
myListeners.remove(listener);
|
||||
}
|
||||
|
||||
protected void notifyListeners() {
|
||||
for (ServiceViewModelListener listener : myListeners) {
|
||||
listener.rootsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Invoker getInvoker() {
|
||||
return myModel.getInvoker();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<? extends ServiceViewItem> filterEmptyGroups(@NotNull List<? extends ServiceViewItem> items) {
|
||||
return ContainerUtil.filter(items, item -> !(item instanceof ServiceGroupNode) || !getChildren(item).isEmpty());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ServiceViewItem findItem(ServiceViewItem viewItem, List<? extends ServiceViewItem> modelItems) {
|
||||
return findItem(getPath(viewItem), modelItems);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ServiceViewItem findItem(Deque<ServiceViewItem> path, List<? extends ServiceViewItem> modelItems) {
|
||||
ServiceViewItem node = path.removeFirst();
|
||||
for (ServiceViewItem root : modelItems) {
|
||||
if (root.equals(node)) {
|
||||
if (path.isEmpty()) {
|
||||
return root;
|
||||
}
|
||||
else {
|
||||
return findItem(path, root.getChildren());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Deque<ServiceViewItem> getPath(ServiceViewItem item) {
|
||||
Deque<ServiceViewItem> path = new LinkedList<>();
|
||||
do {
|
||||
path.addFirst(item);
|
||||
item = item.getParent();
|
||||
}
|
||||
while (item != null);
|
||||
return path;
|
||||
}
|
||||
|
||||
interface ServiceViewModelListener {
|
||||
void rootsChanged();
|
||||
}
|
||||
|
||||
static <T> List<ServiceTreeNode> getContributorChildren(Project project,
|
||||
ServiceTreeNode parent,
|
||||
ServiceViewContributor<T> contributor) {
|
||||
Set<ServiceTreeNode> children = new LinkedHashSet<>();
|
||||
Map<Object, ServiceGroupNode> groupNodes = new HashMap<>();
|
||||
for (T service : contributor.getServices(project)) {
|
||||
Object value = service instanceof ServiceViewProvidingContributor ? ((ServiceViewProvidingContributor)service).asService() : service;
|
||||
|
||||
if (contributor instanceof ServiceViewGroupingContributor) {
|
||||
ServiceViewGroupingContributor<T, Object> groupingContributor = (ServiceViewGroupingContributor<T, Object>)contributor;
|
||||
Object group = groupingContributor.groupBy(service);
|
||||
if (group != null) {
|
||||
ServiceGroupNode groupNode = groupNodes.get(group);
|
||||
if (groupNode == null) {
|
||||
groupNode = new ServiceGroupNode(group, parent, contributor, groupingContributor.getGroupDescriptor(group));
|
||||
groupNodes.put(group, groupNode);
|
||||
}
|
||||
ServiceTreeNode serviceNode = new ServiceNode(value, groupNode, contributor, contributor.getServiceDescriptor(service), project,
|
||||
service instanceof ServiceViewContributor ? (ServiceViewContributor)service : null);
|
||||
groupNode.getChildren().add(serviceNode);
|
||||
children.add(groupNode);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ServiceTreeNode serviceNode = new ServiceNode(value, parent, contributor, contributor.getServiceDescriptor(service), project,
|
||||
service instanceof ServiceViewContributor ? (ServiceViewContributor)service : null);
|
||||
children.add(serviceNode);
|
||||
}
|
||||
return new ArrayList<>(children);
|
||||
}
|
||||
|
||||
class AllServicesModel implements ServiceViewModel {
|
||||
private final Project myProject;
|
||||
private final Invoker myInvoker = new Invoker.BackgroundThread(this);
|
||||
private final List<ServiceViewModelListener> myListeners = ContainerUtil.newSmartList();
|
||||
private final List<ServiceTreeNode> myRoots = new ArrayList<>();
|
||||
private boolean myRootInitialized;
|
||||
|
||||
public AllServicesModel(Project project) {
|
||||
myProject = project;
|
||||
static class AllServicesModel extends ServiceViewModel {
|
||||
AllServicesModel(@NotNull ServiceModel model, @NotNull ServiceModelFilter modelFilter) {
|
||||
super(model, modelFilter, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends ServiceTreeNode> getRoots() {
|
||||
if (!myRootInitialized) {
|
||||
myRootInitialized = true;
|
||||
myRoots.clear();
|
||||
myRoots.addAll(doGetRoots());
|
||||
}
|
||||
return myRoots;
|
||||
}
|
||||
|
||||
private List<? extends ServiceTreeNode> doGetRoots() {
|
||||
List<ServiceTreeNode> result = new ArrayList<>();
|
||||
for (ServiceViewContributor<?> contributor : ServiceViewManagerImpl.EP_NAME.getExtensions()) {
|
||||
result.addAll(getContributorChildren(myProject, null, contributor));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(@NotNull ServiceEventListener.ServiceEvent e) {
|
||||
getInvoker().runOrInvokeLater(() -> reset(e.contributorClass));
|
||||
}
|
||||
|
||||
private void reset(Class<?> contributorClass) {
|
||||
int startIndex = -1;
|
||||
|
||||
if (myRoots.isEmpty()) {
|
||||
startIndex = 0;
|
||||
}
|
||||
else {
|
||||
Map<ServiceViewContributor, Integer> indexes = new HashMap<>();
|
||||
List<ServiceTreeNode> toRemove = new ArrayList<>();
|
||||
ServiceViewContributor previous = null;
|
||||
for (int i = 0; i < myRoots.size(); i++) {
|
||||
ServiceTreeNode child = myRoots.get(i);
|
||||
if (contributorClass.isInstance(child.getContributor())) {
|
||||
toRemove.add(child);
|
||||
if (startIndex < 0) {
|
||||
startIndex = i;
|
||||
}
|
||||
}
|
||||
else if (previous != child.getContributor()) {
|
||||
previous = child.getContributor();
|
||||
indexes.put(previous, i);
|
||||
}
|
||||
}
|
||||
if (startIndex < 0) {
|
||||
ServiceViewContributor[] contributors = ServiceViewManagerImpl.EP_NAME.getExtensions();
|
||||
for (int i = contributors.length - 1; i >= 0; i--) {
|
||||
if (!contributorClass.isInstance(contributors[i])) {
|
||||
startIndex = indexes.getOrDefault(contributors[i], Integer.valueOf(-1));
|
||||
if (startIndex == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (startIndex < 0) {
|
||||
startIndex = myRoots.size() - toRemove.size();
|
||||
}
|
||||
}
|
||||
myRoots.removeAll(toRemove);
|
||||
}
|
||||
|
||||
List<ServiceTreeNode> newChildren = null;
|
||||
for (ServiceViewContributor<?> contributor : ServiceViewManagerImpl.EP_NAME.getExtensions()) {
|
||||
if (contributorClass.isInstance(contributor)) {
|
||||
newChildren = getContributorChildren(myProject, null, contributor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (newChildren != null) {
|
||||
myRoots.addAll(startIndex, newChildren);
|
||||
}
|
||||
|
||||
for (ServiceViewModelListener listener : myListeners) {
|
||||
listener.rootsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addModelListener(ServiceViewModelListener listener) {
|
||||
myListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeModelListener(ServiceViewModelListener listener) {
|
||||
myListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected List<? extends ServiceViewItem> doGetRoots() {
|
||||
return myModelFilter.filter(myModel.getRoots(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Invoker getInvoker() {
|
||||
return myInvoker;
|
||||
void eventProcessed(ServiceEvent e) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ServiceTreeNode implements ServiceViewItem {
|
||||
private final Object myValue;
|
||||
private final ServiceTreeNode myParent;
|
||||
static class ContributorModel extends ServiceViewModel {
|
||||
private final ServiceViewContributor myContributor;
|
||||
private final ServiceViewDescriptor myViewDescriptor;
|
||||
private List<ServiceTreeNode> myChildren;
|
||||
private boolean myPresentationUpdated;
|
||||
|
||||
protected ServiceTreeNode(@NotNull Object value, @Nullable ServiceTreeNode parent, @NotNull ServiceViewContributor contributor,
|
||||
@NotNull ServiceViewDescriptor viewDescriptor) {
|
||||
myValue = value;
|
||||
myParent = parent;
|
||||
myContributor = contributor;
|
||||
myViewDescriptor = viewDescriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
ServiceViewContributor getContributor() {
|
||||
return myContributor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ServiceViewDescriptor getViewDescriptor() {
|
||||
if (!myPresentationUpdated) {
|
||||
myPresentationUpdated = true;
|
||||
if (myValue instanceof NodeDescriptor) {
|
||||
((NodeDescriptor)myValue).update();
|
||||
ContributorModel(@NotNull ServiceModel model, @NotNull ServiceModelFilter modelFilter, @NotNull ServiceViewContributor contributor,
|
||||
@Nullable ServiceViewFilter parentFilter) {
|
||||
super(model, modelFilter, new ServiceViewFilter(parentFilter) {
|
||||
@Override
|
||||
public boolean value(ServiceViewItem item) {
|
||||
return contributor.equals(item.getContributor());
|
||||
}
|
||||
}
|
||||
return myViewDescriptor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ServiceTreeNode getParent() {
|
||||
return myParent;
|
||||
});
|
||||
myContributor = contributor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
List<ServiceTreeNode> getChildren() {
|
||||
if (myChildren == null) {
|
||||
myChildren = doGetChildren();
|
||||
@Override
|
||||
protected List<? extends ServiceViewItem> doGetRoots() {
|
||||
return myModelFilter.filter(ContainerUtil.filter(myModel.getRoots(), getFilter()), getFilter());
|
||||
}
|
||||
|
||||
@Override
|
||||
void eventProcessed(ServiceEvent e) {
|
||||
if (e.contributorClass.isInstance(myContributor)) {
|
||||
notifyListeners();
|
||||
}
|
||||
return myChildren;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
abstract List<ServiceTreeNode> doGetChildren();
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ServiceTreeNode node = (ServiceTreeNode)o;
|
||||
return myValue.equals(node.myValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myValue.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceNode extends ServiceTreeNode {
|
||||
private final Project myProject;
|
||||
private final ServiceViewContributor<?> myProvidingContributor;
|
||||
static class GroupModel extends ServiceViewModel {
|
||||
private final AtomicReference<ServiceGroupNode> myGroupRef;
|
||||
|
||||
ServiceNode(@NotNull Object service, @Nullable ServiceTreeNode parent, @NotNull ServiceViewContributor contributor,
|
||||
@NotNull ServiceViewDescriptor viewDescriptor,
|
||||
@NotNull Project project, @Nullable ServiceViewContributor providingContributor) {
|
||||
super(service, parent, contributor, viewDescriptor);
|
||||
myProject = project;
|
||||
myProvidingContributor = providingContributor;
|
||||
GroupModel(@NotNull ServiceModel model, @NotNull ServiceModelFilter modelFilter,
|
||||
@NotNull AtomicReference<ServiceGroupNode> groupRef, @Nullable ServiceViewFilter parentFilter) {
|
||||
super(model, modelFilter, new ServiceViewFilter(parentFilter) {
|
||||
@Override
|
||||
public boolean value(ServiceViewItem item) {
|
||||
ServiceViewItem parent = item.getParent();
|
||||
return parent != null && parent.equals(groupRef.get());
|
||||
}
|
||||
});
|
||||
myGroupRef = groupRef;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
List<ServiceTreeNode> doGetChildren() {
|
||||
return myProvidingContributor == null ? Collections.emptyList() : getContributorChildren(myProject, this, myProvidingContributor);
|
||||
protected List<? extends ServiceViewItem> doGetRoots() {
|
||||
ServiceGroupNode group = myGroupRef.get();
|
||||
return group == null ? Collections.emptyList() : getChildren(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
void eventProcessed(ServiceEvent e) {
|
||||
ServiceGroupNode group = myGroupRef.get();
|
||||
if (group == null || !e.contributorClass.isInstance(group.getRootContributor())) return;
|
||||
|
||||
myGroupRef.set((ServiceGroupNode)findItem(group, myModel.getRoots()));
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceGroupNode extends ServiceTreeNode {
|
||||
ServiceGroupNode(@NotNull Object group, @Nullable ServiceTreeNode parent, @NotNull ServiceViewContributor contributor,
|
||||
@NotNull ServiceViewDescriptor viewDescriptor) {
|
||||
super(group, parent, contributor, viewDescriptor);
|
||||
static class SingeServiceModel extends ServiceViewModel {
|
||||
private final AtomicReference<ServiceViewItem> myServiceRef;
|
||||
|
||||
SingeServiceModel(@NotNull ServiceModel model, @NotNull ServiceModelFilter modelFilter,
|
||||
@NotNull AtomicReference<ServiceViewItem> serviceRef, @Nullable ServiceViewFilter parentFilter) {
|
||||
super(model, modelFilter, new ServiceViewFilter(parentFilter) {
|
||||
@Override
|
||||
public boolean value(ServiceViewItem item) {
|
||||
return item.equals(serviceRef.get());
|
||||
}
|
||||
});
|
||||
myServiceRef = serviceRef;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
List<ServiceTreeNode> doGetChildren() {
|
||||
return new ArrayList<>();
|
||||
protected List<? extends ServiceViewItem> doGetRoots() {
|
||||
ServiceViewItem service = myServiceRef.get();
|
||||
return service == null ? Collections.emptyList() : Collections.singletonList(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
void eventProcessed(ServiceEvent e) {
|
||||
ServiceViewItem service = myServiceRef.get();
|
||||
if (service == null || !e.contributorClass.isInstance(service.getRootContributor())) return;
|
||||
|
||||
myServiceRef.set(findItem(service, myModel.getRoots()));
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
static class ServiceListModel extends ServiceViewModel {
|
||||
private final List<ServiceViewItem> myRoots;
|
||||
|
||||
ServiceListModel(@NotNull ServiceModel model, @NotNull ServiceModelFilter modelFilter, @NotNull List<ServiceViewItem> roots,
|
||||
@Nullable ServiceViewFilter parentFilter) {
|
||||
super(model, modelFilter, new ServiceViewFilter(parentFilter) {
|
||||
@Override
|
||||
public boolean value(ServiceViewItem item) {
|
||||
return roots.contains(item);
|
||||
}
|
||||
});
|
||||
myRoots = roots;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<? extends ServiceViewItem> doGetRoots() {
|
||||
return myModelFilter.filter(myRoots, getFilter());
|
||||
}
|
||||
|
||||
@Override
|
||||
void eventProcessed(ServiceEvent e) {
|
||||
boolean update = false;
|
||||
|
||||
List<ServiceViewItem> toRemove = new ArrayList<>();
|
||||
for (int i = 0; i < myRoots.size(); i++) {
|
||||
ServiceViewItem node = myRoots.get(i);
|
||||
if (!e.contributorClass.isInstance(node.getRootContributor())) continue;
|
||||
|
||||
ServiceViewItem updatedNode = findItem(node, myModel.getRoots());
|
||||
if (updatedNode != null) {
|
||||
//noinspection SuspiciousListRemoveInLoop
|
||||
myRoots.remove(i);
|
||||
myRoots.add(i, updatedNode);
|
||||
}
|
||||
else {
|
||||
toRemove.add(node);
|
||||
}
|
||||
update = true;
|
||||
}
|
||||
myRoots.removeAll(toRemove);
|
||||
|
||||
if (update) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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.execution.services;
|
||||
|
||||
import com.intellij.openapi.ui.SimpleToolWindowPanel;
|
||||
import com.intellij.ui.components.JBPanelWithEmptyText;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class ServiceViewSingleUi implements ServiceViewUi {
|
||||
private final SimpleToolWindowPanel myMainPanel = new SimpleToolWindowPanel(false);
|
||||
private final JPanel myMessagePanel = new JBPanelWithEmptyText().withEmptyText("No content available");
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return myMainPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveState(@NotNull ServiceViewState state) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServiceToolbar(@NotNull ServiceViewActionProvider actionManager) {
|
||||
myMainPanel.setToolbar(actionManager.createServiceToolbar(myMainPanel));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMasterPanel(@NotNull JComponent component, @NotNull ServiceViewActionProvider actionManager) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDetailsComponent(@Nullable JComponent component) {
|
||||
if (component == null) {
|
||||
component = myMessagePanel;
|
||||
}
|
||||
if (component.getParent() == myMainPanel) return;
|
||||
|
||||
myMainPanel.setContent(component);
|
||||
}
|
||||
}
|
||||
@@ -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.execution.services;
|
||||
|
||||
import com.intellij.execution.services.ServiceModel.ServiceViewItem;
|
||||
import com.intellij.ide.projectView.PresentationData;
|
||||
import com.intellij.navigation.ItemPresentation;
|
||||
import com.intellij.openapi.Disposable;
|
||||
@@ -40,8 +41,6 @@ class ServiceViewTree extends Tree {
|
||||
new TreeSpeedSearch(this, TreeSpeedSearch.NODE_DESCRIPTOR_TOSTRING, true);
|
||||
ServiceViewTreeLinkMouseListener mouseListener = new ServiceViewTreeLinkMouseListener(this);
|
||||
mouseListener.installOn(this);
|
||||
|
||||
//RowsDnDSupport.install(myTree, myTreeModel);
|
||||
new DoubleClickListener() {
|
||||
@Override
|
||||
protected boolean onDoubleClick(MouseEvent e) {
|
||||
@@ -54,6 +53,10 @@ class ServiceViewTree extends Tree {
|
||||
((ServiceViewItem)lastComponent).getViewDescriptor().handleDoubleClick(e);
|
||||
}
|
||||
}.installOn(this);
|
||||
|
||||
// DnD
|
||||
//RowsDnDSupport.install(myTree, myTreeModel);
|
||||
setDragEnabled(true);
|
||||
}
|
||||
|
||||
private static class ServiceViewTreeCellRenderer extends ServiceViewTreeCellRendererBase {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// 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.execution.services;
|
||||
|
||||
import com.intellij.execution.services.ServiceViewModel.ServiceTreeNode;
|
||||
import com.intellij.execution.services.ServiceModel.ServiceViewItem;
|
||||
import com.intellij.execution.services.ServiceViewModel.ServiceViewModelListener;
|
||||
import com.intellij.ui.tree.BaseTreeModel;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Function;
|
||||
@@ -15,16 +16,18 @@ import org.jetbrains.concurrency.AsyncPromise;
|
||||
import org.jetbrains.concurrency.Promise;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class ServiceViewTreeModel extends BaseTreeModel<Object> implements InvokerSupplier {
|
||||
private final ServiceViewModel myModel;
|
||||
private final ServiceViewModel.ServiceViewModelListener myListener;
|
||||
private final ServiceViewModelListener myListener;
|
||||
private final Object myRoot = ObjectUtils.sentinel("services root");
|
||||
|
||||
ServiceViewTreeModel(ServiceViewModel model) {
|
||||
myModel = model;
|
||||
myListener = new ServiceViewModel.ServiceViewModelListener() {
|
||||
myListener = new ServiceViewModelListener() {
|
||||
@Override
|
||||
public void rootsChanged() {
|
||||
treeStructureChanged(new TreePath(myRoot), null, null);
|
||||
@@ -46,7 +49,7 @@ class ServiceViewTreeModel extends BaseTreeModel<Object> implements InvokerSuppl
|
||||
|
||||
@Override
|
||||
public boolean isLeaf(Object object) {
|
||||
return object != myRoot && ((ServiceTreeNode)object).getChildren().isEmpty();
|
||||
return object != myRoot && myModel.getChildren(((ServiceViewItem)object)).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,7 +57,7 @@ class ServiceViewTreeModel extends BaseTreeModel<Object> implements InvokerSuppl
|
||||
if (parent == myRoot) {
|
||||
return myModel.getRoots();
|
||||
}
|
||||
return ((ServiceTreeNode)parent).getChildren();
|
||||
return myModel.getChildren(((ServiceViewItem)parent));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,18 +68,20 @@ class ServiceViewTreeModel extends BaseTreeModel<Object> implements InvokerSuppl
|
||||
Promise<TreePath> findPath(@NotNull Object service, @NotNull Class<?> contributorClass) {
|
||||
AsyncPromise<TreePath> result = new AsyncPromise<>();
|
||||
getInvoker().runOrInvokeLater(() -> {
|
||||
ServiceTreeNode serviceNode = JBTreeTraverser.from((Function<ServiceTreeNode, List<ServiceTreeNode>>)node ->
|
||||
contributorClass.isInstance(node.getContributor()) ? node.getChildren() : null)
|
||||
.withRoots(myModel.getRoots())
|
||||
List<? extends ServiceViewItem> roots = myModel.getRoots();
|
||||
ServiceViewItem serviceNode = JBTreeTraverser.from((Function<ServiceViewItem, List<ServiceViewItem>>)node ->
|
||||
contributorClass.isInstance(node.getRootContributor()) ? new ArrayList<>(myModel.getChildren(node)) : null)
|
||||
.withRoots(roots)
|
||||
.traverse(TreeTraversal.PLAIN_BFS)
|
||||
.filter(node -> node.getValue().equals(service))
|
||||
.first();
|
||||
if (serviceNode != null) {
|
||||
List<Object> path = new ArrayList<>();
|
||||
while (serviceNode != null) {
|
||||
do {
|
||||
path.add(serviceNode);
|
||||
serviceNode = serviceNode.getParent();
|
||||
serviceNode = roots.contains(serviceNode) ? null : serviceNode.getParent();
|
||||
}
|
||||
while (serviceNode != null);
|
||||
path.add(myRoot);
|
||||
Collections.reverse(path);
|
||||
result.setResult(new TreePath(ArrayUtil.toObjectArray(path)));
|
||||
|
||||
@@ -322,7 +322,7 @@
|
||||
<separator/>
|
||||
<add-to-group group-id="MainMenu" anchor="after" relative-to-action="GoToMenu"/>
|
||||
</group>
|
||||
|
||||
|
||||
<group id="ParameterNameHints" popup="true">
|
||||
<action id="ShowSettingsWithAddedPattern" class="com.intellij.codeInsight.hints.ShowSettingsWithAddedPattern"/>
|
||||
<action id="ToggleInlineHintsAction" class="com.intellij.codeInsight.hints.ToggleInlineHintsAction"/>
|
||||
@@ -330,7 +330,7 @@
|
||||
<separator/>
|
||||
<action id="ToggleCompletionHintsAction" class="com.intellij.codeInsight.hints.ToggleCompletionHintsAction"/>
|
||||
</group>
|
||||
|
||||
|
||||
<!-- Analyze -->
|
||||
<action id="SliceBackward" class="com.intellij.slicer.SliceBackwardAction"/>
|
||||
<action id="SliceForward" class="com.intellij.slicer.SliceForwardAction"/>
|
||||
@@ -1179,6 +1179,8 @@
|
||||
</group>
|
||||
|
||||
<group id="ServiceViewTreeToolbar">
|
||||
<action id="ExtractServiceAction" class="com.intellij.execution.services.ExtractServiceAction"
|
||||
text="Extract" icon="AllIcons.Actions.Forward"/>
|
||||
<group id="ServiceViewTreeSettings" popup="true" text="Settings" icon="AllIcons.General.Settings"
|
||||
class="com.intellij.ide.actions.NonEmptyActionGroup"/>
|
||||
</group>
|
||||
|
||||
Reference in New Issue
Block a user