diff --git a/platform/lang-api/src/com/intellij/execution/dashboard/DashboardNode.java b/platform/lang-api/src/com/intellij/execution/dashboard/DashboardNode.java index da9e860565c4..bfdc96ff48ba 100644 --- a/platform/lang-api/src/com/intellij/execution/dashboard/DashboardNode.java +++ b/platform/lang-api/src/com/intellij/execution/dashboard/DashboardNode.java @@ -15,6 +15,8 @@ */ package com.intellij.execution.dashboard; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.openapi.project.Project; import com.intellij.ui.content.Content; import org.jetbrains.annotations.Nullable; @@ -23,5 +25,14 @@ import org.jetbrains.annotations.Nullable; */ public interface DashboardNode { @Nullable - Content getContent(); + default RunContentDescriptor getDescriptor() { + return null; + } + + @Nullable + default Content getContent() { + return getDescriptor() == null ? null : getDescriptor().getAttachedContent(); + } + + Project getProject(); } diff --git a/platform/lang-api/src/com/intellij/execution/dashboard/DashboardRunConfigurationNode.java b/platform/lang-api/src/com/intellij/execution/dashboard/DashboardRunConfigurationNode.java new file mode 100644 index 000000000000..8369c8dbb865 --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/dashboard/DashboardRunConfigurationNode.java @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard; + +import com.intellij.execution.RunnerAndConfigurationSettings; +import org.jetbrains.annotations.NotNull; + +/** + * @author konstantin.aleev + */ +public interface DashboardRunConfigurationNode extends DashboardNode { + @NotNull + RunnerAndConfigurationSettings getConfigurationSettings(); + + boolean isTerminated(); +} diff --git a/platform/lang-api/src/com/intellij/execution/dashboard/DashboardTreeAction.java b/platform/lang-api/src/com/intellij/execution/dashboard/DashboardTreeAction.java new file mode 100644 index 000000000000..a958ba8dfbf5 --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/dashboard/DashboardTreeAction.java @@ -0,0 +1,150 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard; + +import com.intellij.ide.util.treeView.AbstractTreeBuilder; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * @author konstantin.aleev + */ +public abstract class DashboardTreeAction extends AnAction { + protected DashboardTreeAction(String text, String description, Icon icon) { + super(text, description, icon); + } + + @Override + public void update(@NotNull AnActionEvent e) { + Presentation presentation = e.getPresentation(); + List targetNodes = getTargetNodes(e); + + boolean visible; + boolean enabled; + + if (targetNodes == null) { + visible = false; + enabled = false; + } + else { + visible = true; + enabled = true; + for (T targetNode : targetNodes) { + visible &= isVisible4(targetNode); + enabled &= visible && isEnabled4(targetNode); + } + } + + presentation.setVisible(visible); + presentation.setEnabled(enabled); + updatePresentation(presentation, ContainerUtil.getFirstItem(targetNodes)); + } + + /** + * Invokes {@link #collectNodes(AbstractTreeBuilder) collectNodes()} to collect nodes. + * If each collected node could be casted to tree action node class, + * returns a list of collected nodes casted to tree action node class, otherwise returns {@code null}. + * + * @param e Action event. + * @return List of target nodes for this action. + */ + @Nullable + protected List getTargetNodes(AnActionEvent e) { + C content = getTreeContent(e); + if (content == null) { + return null; + } + Set selectedElements = collectNodes(content.getBuilder()); + int selectionCount = selectedElements.size(); + if (selectionCount == 0 || selectionCount > 1 && !isMultiSelectionAllowed()) { + return null; + } + Class targetNodeClass = getTargetNodeClass(); + List result = new ArrayList<>(); + for (Object selectedElement : selectedElements) { + if (!targetNodeClass.isInstance(selectedElement)) { + return null; + } + result.add(targetNodeClass.cast(selectedElement)); + } + return result; + } + + /** + * This implementation returns a set of selected nodes. + * Subclasses may override this method to return modified nodes set. + * + * @param treeBuilder Tree builder. + * @return Set of tree nodes for which action should be performed. + */ + @NotNull + protected Set collectNodes(@NotNull AbstractTreeBuilder treeBuilder) { + return treeBuilder.getSelectedElements(); + } + + protected abstract C getTreeContent(AnActionEvent e); + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + List targetNodes = getTargetNodes(e); + if (targetNodes == null) { + return; + } + + List verifiedTargetNodes = ContainerUtil.filter(targetNodes, targetNode -> isVisible4(targetNode) && isEnabled4(targetNode)); + doActionPerformed(getTreeContent(e), e, verifiedTargetNodes); + } + + protected boolean isMultiSelectionAllowed() { + return false; + } + + protected boolean isVisible4(T node) { + return true; + } + + protected boolean isEnabled4(T node) { + return true; + } + + protected void updatePresentation(@NotNull Presentation presentation, @Nullable T node) { + } + + protected void doActionPerformed(@NotNull C content, AnActionEvent e, List nodes) { + for (T node : nodes) { + doActionPerformed(content, e, node); + } + } + + protected void doActionPerformed(@NotNull C content, AnActionEvent e, T node) { + doActionPerformed(node); + } + + protected void doActionPerformed(T node) { + throw new UnsupportedOperationException(); + } + + protected abstract Class getTargetNodeClass(); +} diff --git a/platform/lang-api/src/com/intellij/execution/dashboard/Group.java b/platform/lang-api/src/com/intellij/execution/dashboard/Group.java new file mode 100644 index 000000000000..a9cb9c9fb23b --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/dashboard/Group.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard; + +import javax.swing.*; + +/** + * @author konstantin.aleev + */ +public interface Group { + String getName(); + + Icon getIcon(); +} diff --git a/platform/lang-api/src/com/intellij/execution/dashboard/GroupingRule.java b/platform/lang-api/src/com/intellij/execution/dashboard/GroupingRule.java new file mode 100644 index 000000000000..d316cd74d010 --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/dashboard/GroupingRule.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard; + +import com.intellij.ide.util.treeView.AbstractTreeNode; +import com.intellij.ide.util.treeView.smartTree.TreeAction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +/** + * Action for grouping items in a runtime dashboard tree. + * + * @author konstantin.aleev + */ +public interface GroupingRule extends TreeAction { + /** + * @return A list of groups which should be shown in the tree even if they do not contain any nodes. + */ + @NotNull + default List getPermanentGroups() { + return Collections.emptyList(); + } + + /** + * @param node Node which should be grouped by this grouping rule. + * @return A group which node belongs to or null if node could not be grouped by this rule. + */ + @Nullable + Group getGroup(AbstractTreeNode node); +} diff --git a/platform/lang-api/src/com/intellij/execution/dashboard/PanelContentUI.java b/platform/lang-api/src/com/intellij/execution/dashboard/PanelContentUI.java index de289574ae83..5f38abb91730 100644 --- a/platform/lang-api/src/com/intellij/execution/dashboard/PanelContentUI.java +++ b/platform/lang-api/src/com/intellij/execution/dashboard/PanelContentUI.java @@ -44,10 +44,11 @@ class PanelContentUI implements ContentUI { manager.addContentManagerListener(new ContentManagerAdapter() { @Override public void selectionChanged(final ContentManagerEvent event) { - if (ContentManagerEvent.ContentOperation.add != event.getOperation()) { - return; + if (ContentManagerEvent.ContentOperation.add == event.getOperation()) { + showContent(event.getContent()); + } else if (ContentManagerEvent.ContentOperation.remove == event.getOperation()) { + hideContent(); } - showContent(event.getContent()); } }); } @@ -63,6 +64,12 @@ class PanelContentUI implements ContentUI { } } + private void hideContent() { + myPanel.removeAll(); + myPanel.revalidate(); + myPanel.repaint(); + } + @Override public boolean isSingleSelection() { return true; diff --git a/platform/lang-api/src/com/intellij/execution/dashboard/TreeContent.java b/platform/lang-api/src/com/intellij/execution/dashboard/TreeContent.java new file mode 100644 index 000000000000..5d437e96bcb2 --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/dashboard/TreeContent.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard; + +import com.intellij.ide.util.treeView.AbstractTreeBuilder; +import org.jetbrains.annotations.NotNull; + +/** + * @author konstantin.aleev + */ +public interface TreeContent { + @NotNull + AbstractTreeBuilder getBuilder(); +} diff --git a/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java b/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java index 00f09fec7bf9..5c2527b60a33 100644 --- a/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java +++ b/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java @@ -19,6 +19,7 @@ import com.intellij.execution.ExecutionBundle; import com.intellij.execution.ExecutionManager; import com.intellij.execution.KillableProcess; import com.intellij.execution.configurations.RunProfile; +import com.intellij.execution.impl.ExecutionManagerImpl; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.icons.AllIcons; @@ -124,7 +125,7 @@ class StopAction extends DumbAwareAction implements AnAction.TransparentUpdate { int todoSize = cancellableProcesses.size() + stoppableDescriptors.size(); if (todoSize == 1) { if (!stoppableDescriptors.isEmpty()) { - stopProcess(stoppableDescriptors.get(0)); + ExecutionManagerImpl.stopProcess(stoppableDescriptors.get(0)); } else { cancellableProcesses.get(0).second.cancel(); } @@ -182,12 +183,12 @@ class StopAction extends DumbAwareAction implements AnAction.TransparentUpdate { } } else { - stopProcess(getRecentlyStartedContentDescriptor(dataContext)); + ExecutionManagerImpl.stopProcess(getRecentlyStartedContentDescriptor(dataContext)); } } @NotNull - private static List> getCancellableProcesses(@Nullable Project project) { + private static List> getCancellableProcesses(@Nullable Project project) { IdeFrame frame = ((WindowManagerEx)WindowManager.getInstance()).findFrameFor(project); StatusBarEx statusBar = frame == null ? null : (StatusBarEx)frame.getStatusBar(); if (statusBar == null) return Collections.emptyList(); @@ -212,7 +213,7 @@ class StopAction extends DumbAwareAction implements AnAction.TransparentUpdate { HandlerItem item = new HandlerItem(descriptor.getDisplayName(), descriptor.getIcon(), false) { @Override void stop() { - stopProcess(descriptor); + ExecutionManagerImpl.stopProcess(descriptor); } }; items.add(item); @@ -235,22 +236,6 @@ class StopAction extends DumbAwareAction implements AnAction.TransparentUpdate { return Pair.create(items, selected); } - private static void stopProcess(@Nullable RunContentDescriptor descriptor) { - ProcessHandler processHandler = descriptor != null ? descriptor.getProcessHandler() : null; - if (processHandler == null) return; - if (processHandler instanceof KillableProcess && processHandler.isProcessTerminating()) { - ((KillableProcess)processHandler).killProcess(); - return; - } - - if (processHandler.detachIsDefault()) { - processHandler.detachProcess(); - } - else { - processHandler.destroyProcess(); - } - } - @Nullable static RunContentDescriptor getRecentlyStartedContentDescriptor(@NotNull DataContext dataContext) { final RunContentDescriptor contentDescriptor = LangDataKeys.RUN_CONTENT_DESCRIPTOR.getData(dataContext); diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/RuntimeDashboardContent.java b/platform/lang-impl/src/com/intellij/execution/dashboard/RuntimeDashboardContent.java index 0bfcf0258274..0dd39e0a83e0 100644 --- a/platform/lang-impl/src/com/intellij/execution/dashboard/RuntimeDashboardContent.java +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/RuntimeDashboardContent.java @@ -16,24 +16,30 @@ package com.intellij.execution.dashboard; import com.intellij.execution.*; +import com.intellij.execution.dashboard.tree.Grouper; import com.intellij.execution.dashboard.tree.RuntimeDashboardTreeStructure; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.ide.CommonActionsManager; +import com.intellij.ide.DataManager; +import com.intellij.ide.DefaultTreeExpander; +import com.intellij.ide.TreeExpander; import com.intellij.ide.util.treeView.*; +import com.intellij.ide.util.treeView.smartTree.ActionPresentation; import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; -import com.intellij.ui.OnePixelSplitter; -import com.intellij.ui.ScrollPaneFactory; -import com.intellij.ui.SideBorder; -import com.intellij.ui.TreeSpeedSearch; +import com.intellij.ui.*; import com.intellij.ui.components.JBPanelWithEmptyText; import com.intellij.ui.content.*; import com.intellij.ui.treeStructure.Tree; import com.intellij.util.ObjectUtils; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -44,12 +50,18 @@ import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import java.awt.*; import java.util.HashSet; +import java.util.List; import java.util.Set; /** * @author konstantin.aleev */ -class RuntimeDashboardContent extends JPanel implements Disposable { +public class RuntimeDashboardContent extends JPanel implements TreeContent, Disposable { + public static final DataKey KEY = DataKey.create("runtimeDashboardContent"); + @NonNls private static final String PLACE_TOOLBAR = "RuntimeDashboardContent#Toolbar"; + @NonNls private static final String RUNTIME_DASHBOARD_TOOLBAR = "RuntimeDashboardToolbar"; + @NonNls private static final String RUNTIME_DASHBOARD_POPUP = "RuntimeDashboardPopup"; + private static final String MESSAGE_CARD = "message"; private static final String CONTENT_CARD = "content"; @@ -62,15 +74,17 @@ class RuntimeDashboardContent extends JPanel implements Disposable { private AbstractTreeBuilder myBuilder; private AbstractTreeNode myLastSelection; private Set myCollapsedTreeNodeValues = new HashSet<>(); + private List myGroupers; @NotNull private final ContentManager myContentManager; @NotNull private final ContentManagerListener myContentManagerListener; @NotNull private final Project myProject; - public RuntimeDashboardContent(@NotNull Project project, @NotNull ContentManager contentManager) { + public RuntimeDashboardContent(@NotNull Project project, @NotNull ContentManager contentManager, @NotNull List groupers) { super(new BorderLayout()); myProject = project; + myGroupers = groupers; myTreeModel = new DefaultTreeModel(new DefaultMutableTreeNode()); myTree = new Tree(myTreeModel); @@ -80,7 +94,7 @@ class RuntimeDashboardContent extends JPanel implements Disposable { myTree.setCellRenderer(new NodeRenderer()); myTree.setLineStyleAngled(); - //TODO [konstantin.aleev] Create toolbar. + add(createToolbar(), BorderLayout.WEST); Splitter splitter = new OnePixelSplitter(false, 0.3f); splitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT)); @@ -146,7 +160,10 @@ class RuntimeDashboardContent extends JPanel implements Disposable { } }); - //TODO [konstantin.aleev] setup popup actions. + DefaultActionGroup popupActionGroup = new DefaultActionGroup(); + popupActionGroup.add(ActionManager.getInstance().getAction(RUNTIME_DASHBOARD_TOOLBAR)); + popupActionGroup.add(ActionManager.getInstance().getAction(RUNTIME_DASHBOARD_POPUP)); + PopupHandler.installPopupHandler(myTree, popupActionGroup, ActionPlaces.UNKNOWN, ActionManager.getInstance()); new TreeSpeedSearch(myTree, TreeSpeedSearch.NODE_DESCRIPTOR_TOSTRING, true); } @@ -198,7 +215,7 @@ class RuntimeDashboardContent extends JPanel implements Disposable { } private void setupBuilder() { - RuntimeDashboardTreeStructure structure = new RuntimeDashboardTreeStructure(myProject); + RuntimeDashboardTreeStructure structure = new RuntimeDashboardTreeStructure(myProject, myGroupers); myBuilder = new AbstractTreeBuilder(myTree, myTreeModel, structure, IndexComparator.INSTANCE) { @Override protected boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) { @@ -231,7 +248,10 @@ class RuntimeDashboardContent extends JPanel implements Disposable { } @Override - public void processTerminated(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler, int exitCode) { + public void processTerminated(@NotNull String executorId, + @NotNull ExecutionEnvironment env, + @NotNull ProcessHandler handler, + int exitCode) { updateTreeIfNeeded(env.getRunnerAndConfigurationSettings()); } }); @@ -243,11 +263,98 @@ class RuntimeDashboardContent extends JPanel implements Disposable { } } + private JComponent createToolbar() { + JPanel toolBarPanel = new JPanel(new GridLayout()); + DefaultActionGroup leftGroup = new DefaultActionGroup(); + leftGroup.add(ActionManager.getInstance().getAction(RUNTIME_DASHBOARD_TOOLBAR)); + // TODO [konstantin.aleev] provide context help ID + //leftGroup.add(new Separator()); + //leftGroup.add(new ContextHelpAction(HELP_ID)); + + ActionToolbar leftActionToolBar = ActionManager.getInstance().createActionToolbar(PLACE_TOOLBAR, leftGroup, false); + toolBarPanel.add(leftActionToolBar.getComponent()); + + myTree.putClientProperty(DataManager.CLIENT_PROPERTY_DATA_PROVIDER, new DataProvider() { + @Override + public Object getData(@NonNls String dataId) { + if (KEY.getName().equals(dataId)) { + return RuntimeDashboardContent.this; + } + return null; + } + }); + leftActionToolBar.setTargetComponent(myTree); + + DefaultActionGroup rightGroup = new DefaultActionGroup(); + + TreeExpander treeExpander = new DefaultTreeExpander(myTree); + AnAction expandAllAction = CommonActionsManager.getInstance().createExpandAllAction(treeExpander, this); + rightGroup.add(expandAllAction); + + AnAction collapseAllAction = CommonActionsManager.getInstance().createCollapseAllAction(treeExpander, this); + rightGroup.add(collapseAllAction); + + rightGroup.add(new Separator()); + myGroupers.forEach(grouper -> rightGroup.add(new GroupAction(grouper))); + + ActionToolbar rightActionToolBar = ActionManager.getInstance().createActionToolbar(PLACE_TOOLBAR, rightGroup, false); + toolBarPanel.add(rightActionToolBar.getComponent()); + rightActionToolBar.setTargetComponent(myTree); + return toolBarPanel; + } + @Override public void dispose() { } public void updateTree() { - ApplicationManager.getApplication().invokeLater(myBuilder::queueUpdate, myProject.getDisposed()); + ApplicationManager.getApplication().invokeLater(() -> myBuilder.queueUpdate().doWhenDone(() -> { + // Remove nodes not presented in the tree from collapsed node values set. + // Children retrieving is quick since grouping and run configuration nodes are already constructed. + Set nodes = new HashSet<>(); + myBuilder.accept(AbstractTreeNode.class, new TreeVisitor() { + @Override + public boolean visit(@NotNull AbstractTreeNode node) { + nodes.add(node.getValue()); + return false; + } + }); + myCollapsedTreeNodeValues.retainAll(nodes); + }), myProject.getDisposed()); + } + + @NotNull + public AbstractTreeBuilder getBuilder() { + return myBuilder; + } + + private class GroupAction extends ToggleAction implements DumbAware { + private Grouper myGrouper; + + public GroupAction(Grouper grouper) { + super(); + myGrouper = grouper; + } + + @Override + public void update(@NotNull AnActionEvent e) { + super.update(e); + Presentation presentation = e.getPresentation(); + ActionPresentation actionPresentation = myGrouper.getRule().getPresentation(); + presentation.setText(actionPresentation.getText()); + presentation.setDescription(actionPresentation.getDescription()); + presentation.setIcon(actionPresentation.getIcon()); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myGrouper.isEnabled(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + myGrouper.setEnabled(state); + updateTree(); + } } } diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/RuntimeDashboardManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/dashboard/RuntimeDashboardManagerImpl.java index ea03fa3742b2..cc733b0cdb8f 100644 --- a/platform/lang-impl/src/com/intellij/execution/dashboard/RuntimeDashboardManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/RuntimeDashboardManagerImpl.java @@ -15,8 +15,16 @@ */ package com.intellij.execution.dashboard; +import com.intellij.execution.dashboard.tree.ConfigurationTypeGroupingRule; +import com.intellij.execution.dashboard.tree.FolderGroupingRule; +import com.intellij.execution.dashboard.tree.Grouper; +import com.intellij.execution.dashboard.tree.StatusGroupingRule; import com.intellij.icons.AllIcons; import com.intellij.openapi.application.ApplicationManager; +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.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.registry.Registry; @@ -28,16 +36,30 @@ import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; import com.intellij.ui.content.ContentManager; import com.intellij.ui.content.ContentUI; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.ArrayList; +import java.util.List; /** * @author konstantin.aleev */ -public class RuntimeDashboardManagerImpl implements RuntimeDashboardManager { +@State( + name = "RuntimeDashboard", + storages = @Storage(StoragePathMacros.WORKSPACE_FILE) +) +public class RuntimeDashboardManagerImpl implements RuntimeDashboardManager, PersistentStateComponent { + @NonNls private static final String GROUPERS_TAG = "groupers"; + @NonNls private static final String GROUPER_TAG = "grouper"; + @NonNls private static final String NAME_ATTR = "name"; + @NonNls private static final String ENABLED_ATTR = "enabled"; @NotNull private final ContentManager myContentManager; + private List myGroupers = new ArrayList<>(); public RuntimeDashboardManagerImpl(@NotNull final Project project) { ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); @@ -49,7 +71,11 @@ public class RuntimeDashboardManagerImpl implements RuntimeDashboardManager { project, true); toolWindow.setIcon(getToolWindowIcon()); if (!ApplicationManager.getApplication().isUnitTestMode()) { - RuntimeDashboardContent dashboardContent = new RuntimeDashboardContent(project, myContentManager); + myGroupers.add(new Grouper(new ConfigurationTypeGroupingRule())); + myGroupers.add(new Grouper(new StatusGroupingRule())); + myGroupers.add(new Grouper(new FolderGroupingRule())); + + RuntimeDashboardContent dashboardContent = new RuntimeDashboardContent(project, myContentManager, myGroupers); Content content = contentFactory.createContent(dashboardContent, null, false); Disposer.register(content, dashboardContent); toolWindow.getContentManager().addContent(content); @@ -76,4 +102,40 @@ public class RuntimeDashboardManagerImpl implements RuntimeDashboardManager { public Icon getToolWindowIcon() { return AllIcons.Toolwindows.ToolWindowRun; // TODO [konstantin.aleev] provide new icon } + + @Nullable + @Override + public Element getState() { + final Element element = new Element("state"); + final Element groupers = new Element(GROUPERS_TAG); + element.addContent(groupers); + myGroupers.forEach(grouper -> groupers.addContent(writeGrouperState(grouper))); + return element; + } + + private static Element writeGrouperState(Grouper grouper) { + Element element = new Element(GROUPER_TAG); + element.setAttribute(NAME_ATTR, grouper.getRule().getName()); + element.setAttribute(ENABLED_ATTR, Boolean.toString(grouper.isEnabled())); + return element; + } + + @Override + public void loadState(Element element) { + Element groupersElement = element.getChild(GROUPERS_TAG); + if (groupersElement != null) { + List groupers = groupersElement.getChildren(GROUPER_TAG); + groupers.forEach(this::readGrouperState); + } + } + + private void readGrouperState(Element grouperElement) { + String id = grouperElement.getAttributeValue(NAME_ATTR); + for (Grouper grouper : myGroupers) { + if (grouper.getRule().getName().equals(id)) { + grouper.setEnabled(Boolean.valueOf(grouperElement.getAttributeValue(ENABLED_ATTR, "true"))); + return; + } + } + } } diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/CopyConfigurationAction.java b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/CopyConfigurationAction.java new file mode 100644 index 000000000000..eda6320e24a7 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/CopyConfigurationAction.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.actions; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.RunManagerEx; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.configuration.ConfigurationFactoryEx; +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.dashboard.DashboardRunConfigurationNode; +import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl; +import com.intellij.util.PlatformIcons; + +/** + * @author konstantin.aleev + */ +public class CopyConfigurationAction extends RunConfigurationTreeAction { + public CopyConfigurationAction() { + super(ExecutionBundle.message("copy.configuration.action.name"), + ExecutionBundle.message("copy.configuration.action.name"), + PlatformIcons.COPY_ICON); + } + + @Override + @SuppressWarnings("unchecked") + protected void doActionPerformed(DashboardRunConfigurationNode node) { + RunManagerEx runManager = RunManagerEx.getInstanceEx(node.getProject()); + RunnerAndConfigurationSettings settings = node.getConfigurationSettings(); + + RunnerAndConfigurationSettings copiedSettings = ((RunnerAndConfigurationSettingsImpl)settings).clone(); + runManager.setUniqueNameIfNeed(copiedSettings); + copiedSettings.setFolderName(settings.getFolderName()); + + final ConfigurationFactory factory = settings.getFactory(); + if (factory instanceof ConfigurationFactoryEx) { + ((ConfigurationFactoryEx)factory).onConfigurationCopied(settings.getConfiguration()); + } + + runManager.addConfiguration(copiedSettings, runManager.isConfigurationShared(settings), + runManager.getBeforeRunTasks(settings.getConfiguration()), false); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/DebugAction.java b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/DebugAction.java new file mode 100644 index 000000000000..9e842cd510b2 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/DebugAction.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.actions; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.Executor; +import com.intellij.execution.ExecutorRegistry; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.wm.ToolWindowId; + +/** + * @author konstantin.aleev + */ +public class DebugAction extends ExecutorAction { + public DebugAction() { + super(ExecutionBundle.message("runtime.dashboard.debug.action.name"), + ExecutionBundle.message("runtime.dashboard.debug.action.name"), + AllIcons.Toolwindows.ToolWindowDebugger); + } + + @Override + protected Executor getExecutor() { + return ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.DEBUG); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/EditConfigurationAction.java b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/EditConfigurationAction.java new file mode 100644 index 000000000000..057a7e675da3 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/EditConfigurationAction.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.actions; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.dashboard.DashboardRunConfigurationNode; +import com.intellij.execution.impl.RunDialog; +import com.intellij.icons.AllIcons; + +public class EditConfigurationAction extends RunConfigurationTreeAction { + public EditConfigurationAction() { + super(ExecutionBundle.message("runtime.dashboard.edit.configuration.action.name"), + ExecutionBundle.message("runtime.dashboard.edit.configuration.action.name"), + AllIcons.Actions.EditSource); + } + + @Override + protected void doActionPerformed(DashboardRunConfigurationNode node) { + RunDialog.editConfiguration(node.getProject(), node.getConfigurationSettings(), + ExecutionBundle.message("runtime.dashboard.edit.configuration.dialog.title")); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/ExecutorAction.java b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/ExecutorAction.java new file mode 100644 index 000000000000..6c50aab07137 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/ExecutorAction.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.actions; + +import com.intellij.execution.ExecutionManager; +import com.intellij.execution.ExecutionTargetManager; +import com.intellij.execution.Executor; +import com.intellij.execution.ProgramRunnerUtil; +import com.intellij.execution.dashboard.DashboardRunConfigurationNode; +import com.intellij.execution.runners.ProgramRunner; +import com.intellij.execution.ui.RunContentDescriptor; + +import javax.swing.*; + +/** + * @author konstantin.aleev + */ +public abstract class ExecutorAction extends RuntimeDashboardTreeLeafAction { + protected ExecutorAction(String text, String description, Icon icon) { + super(text, description, icon); + } + + @Override + protected boolean isEnabled4(DashboardRunConfigurationNode node) { + String executorId = getExecutor().getId(); + ProgramRunner runner = ProgramRunnerUtil.getRunner(executorId, node.getConfigurationSettings()); + return runner != null && runner.canRun(executorId, node.getConfigurationSettings().getConfiguration()); + } + + @Override + protected void doActionPerformed(DashboardRunConfigurationNode node) { + RunContentDescriptor descriptor = node.getDescriptor(); + ExecutionManager.getInstance(node.getProject()).restartRunProfile(node.getProject(), + getExecutor(), + ExecutionTargetManager.getActiveTarget(node.getProject()), + node.getConfigurationSettings(), + descriptor == null ? null : descriptor.getProcessHandler()); + } + + @Override + protected Class getTargetNodeClass() { + return DashboardRunConfigurationNode.class; + } + + protected abstract Executor getExecutor(); +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RemoveConfigurationAction.java b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RemoveConfigurationAction.java new file mode 100644 index 000000000000..8d1abca6c456 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RemoveConfigurationAction.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.actions; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.RunManagerEx; +import com.intellij.execution.dashboard.DashboardRunConfigurationNode; +import com.intellij.execution.dashboard.RuntimeDashboardContent; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author konstantin.aleev + */ +public class RemoveConfigurationAction extends RunConfigurationTreeAction { + public RemoveConfigurationAction() { + super(ExecutionBundle.message("runtime.dashboard.remove.configuration.action.name"), + ExecutionBundle.message("runtime.dashboard.remove.configuration.action.name"), + AllIcons.General.Remove); + } + + @Override + protected boolean isEnabled4(DashboardRunConfigurationNode node) { + return node.isTerminated(); + } + + @Override + protected boolean isMultiSelectionAllowed() { + return true; + } + + @Override + protected void doActionPerformed(@NotNull RuntimeDashboardContent content, AnActionEvent e, List nodes) { + if (Messages.showYesNoDialog((Project)null, + ExecutionBundle.message("runtime.dashboard.remove.configuration.dialog.message"), + ExecutionBundle.message("runtime.dashboard.remove.configuration.dialog.title"), + Messages.getWarningIcon()) + != Messages.YES) { + return; + } + super.doActionPerformed(content, e, nodes); + } + + @Override + protected void doActionPerformed(DashboardRunConfigurationNode node) { + RunManagerEx.getInstanceEx(node.getProject()).removeConfiguration(node.getConfigurationSettings()); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RunAction.java b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RunAction.java new file mode 100644 index 000000000000..555cc5ffcdd3 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RunAction.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.actions; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.Executor; +import com.intellij.execution.executors.DefaultRunExecutor; +import com.intellij.icons.AllIcons; + +/** + * @author konstantin.aleev + */ +public class RunAction extends ExecutorAction { + public RunAction() { + super(ExecutionBundle.message("runtime.dashboard.run.action.name"), + ExecutionBundle.message("runtime.dashboard.run.action.name"), + AllIcons.Toolwindows.ToolWindowRun); + } + + @Override + protected Executor getExecutor() { + return DefaultRunExecutor.getRunExecutorInstance(); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RunConfigurationTreeAction.java b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RunConfigurationTreeAction.java new file mode 100644 index 000000000000..e1aca3e53e07 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RunConfigurationTreeAction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.actions; + +import com.intellij.execution.dashboard.DashboardRunConfigurationNode; + +import javax.swing.*; + +/** + * @author konstantin.aleev + */ +public abstract class RunConfigurationTreeAction extends RuntimeDashboardTreeAction { + protected RunConfigurationTreeAction(String text, String description, Icon icon) { + super(text, description, icon); + } + + @Override + protected Class getTargetNodeClass() { + return DashboardRunConfigurationNode.class; + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RuntimeDashboardTreeAction.java b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RuntimeDashboardTreeAction.java new file mode 100644 index 000000000000..4faf1d9a08b9 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RuntimeDashboardTreeAction.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.actions; + +import com.intellij.execution.dashboard.*; +import com.intellij.openapi.actionSystem.AnActionEvent; + +import javax.swing.*; + +/** + * @author konstantin.aleev + */ +public abstract class RuntimeDashboardTreeAction extends DashboardTreeAction { + protected RuntimeDashboardTreeAction(String text, String description, Icon icon) { + super(text, description, icon); + } + + @Override + protected final RuntimeDashboardContent getTreeContent(AnActionEvent e) { + return e.getData(RuntimeDashboardContent.KEY); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RuntimeDashboardTreeLeafAction.java b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RuntimeDashboardTreeLeafAction.java new file mode 100644 index 000000000000..729db3c6a565 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/RuntimeDashboardTreeLeafAction.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.actions; + +import com.intellij.execution.dashboard.DashboardNode; +import com.intellij.execution.dashboard.tree.GroupingNode; +import com.intellij.ide.util.treeView.AbstractTreeBuilder; +import com.intellij.ide.util.treeView.AbstractTreeNode; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.*; + +/** + * @author konstantin.aleev + */ +public abstract class RuntimeDashboardTreeLeafAction extends RuntimeDashboardTreeAction { + protected RuntimeDashboardTreeLeafAction(String text, String description, Icon icon) { + super(text, description, icon); + } + + @Override + protected final boolean isMultiSelectionAllowed() { + return true; + } + + @Override + @NotNull + protected Set collectNodes(@NotNull AbstractTreeBuilder treeBuilder) { + Set selectedElement = treeBuilder.getSelectedElements(); + List nodes = new ArrayList<>(); + for (Object o : selectedElement) { + if (!(o instanceof AbstractTreeNode)) { + return Collections.emptySet(); + } + AbstractTreeNode node = (AbstractTreeNode)o; + if (node instanceof GroupingNode && node.getChildren().isEmpty()) { + // Action could not be performed if current selection contains empty grouping nodes + return Collections.emptySet(); + } + nodes.add(node); + } + return getLeaves(nodes); + } + + private static Set getLeaves(Collection nodes) { + Set result = new HashSet<>(); + for (AbstractTreeNode node : nodes) { + Collection children = node.getChildren(); + if (children.isEmpty()) { + if (!(node instanceof GroupingNode)) { + // Do not add grouping nodes to the target set + result.add(node); + } + } else { + result.addAll(getLeaves(children)); + } + } + return result; + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/StopAction.java b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/StopAction.java new file mode 100644 index 000000000000..6d9d568c276a --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/StopAction.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.actions; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.dashboard.DashboardRunConfigurationNode; +import com.intellij.execution.impl.ExecutionManagerImpl; +import com.intellij.icons.AllIcons; + +/** + * @author konstantin.aleev + */ +public class StopAction extends RuntimeDashboardTreeLeafAction { + public StopAction() { + super(ExecutionBundle.message("runtime.dashboard.stop.action.name"), + ExecutionBundle.message("runtime.dashboard.stop.action.name"), + AllIcons.Actions.Suspend); + } + + @Override + protected void doActionPerformed(DashboardRunConfigurationNode node) { + ExecutionManagerImpl.stopProcess(node.getDescriptor()); + } + + @Override + protected Class getTargetNodeClass() { + return DashboardRunConfigurationNode.class; + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/tree/AbstractRunConfigurationNode.java b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/AbstractRunConfigurationNode.java index a9aea6509d1e..0a37eafcda9e 100644 --- a/platform/lang-impl/src/com/intellij/execution/dashboard/tree/AbstractRunConfigurationNode.java +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/AbstractRunConfigurationNode.java @@ -15,15 +15,14 @@ */ package com.intellij.execution.dashboard.tree; +import com.intellij.execution.RunManagerEx; import com.intellij.execution.RunnerAndConfigurationSettings; -import com.intellij.execution.dashboard.DashboardNode; +import com.intellij.execution.dashboard.DashboardRunConfigurationNode; import com.intellij.execution.dashboard.RuntimeDashboardContributor; -import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.ide.projectView.PresentationData; import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.openapi.project.Project; import com.intellij.ui.RowIcon; -import com.intellij.ui.content.Content; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,7 +31,7 @@ import javax.swing.*; /** * @author konstantin.aleev */ -abstract class AbstractRunConfigurationNode extends AbstractTreeNode implements DashboardNode { +abstract class AbstractRunConfigurationNode extends AbstractTreeNode implements DashboardRunConfigurationNode { @NotNull private final RunnerAndConfigurationSettings myConfigurationSettings; protected AbstractRunConfigurationNode(Project project, T value, @NotNull RunnerAndConfigurationSettings configurationSettings) { @@ -43,7 +42,7 @@ abstract class AbstractRunConfigurationNode extends AbstractTreeNode imple @Override protected void update(PresentationData presentation) { presentation.setPresentableText(myConfigurationSettings.getName()); - Icon icon = myConfigurationSettings.getConfiguration().getIcon(); + Icon icon = RunManagerEx.getInstanceEx(getProject()).getConfigurationIcon(myConfigurationSettings); Icon decorator = getIconDecorator(); if (decorator != null) { icon = new RowIcon(icon, decorator); @@ -56,14 +55,11 @@ abstract class AbstractRunConfigurationNode extends AbstractTreeNode imple } @Override - @Nullable - public Content getContent() { - return getDescriptor() == null ? null : getDescriptor().getAttachedContent(); + @NotNull + public RunnerAndConfigurationSettings getConfigurationSettings() { + return myConfigurationSettings; } @Nullable protected abstract Icon getIconDecorator(); - - @Nullable - protected abstract RunContentDescriptor getDescriptor(); } diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/tree/ConfigurationTypeGroupingRule.java b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/ConfigurationTypeGroupingRule.java new file mode 100644 index 000000000000..9e5a675b0ec0 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/ConfigurationTypeGroupingRule.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.tree; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.configurations.ConfigurationType; +import com.intellij.execution.dashboard.Group; +import com.intellij.execution.dashboard.GroupingRule; +import com.intellij.execution.dashboard.DashboardRunConfigurationNode; +import com.intellij.icons.AllIcons; +import com.intellij.ide.util.treeView.AbstractTreeNode; +import com.intellij.ide.util.treeView.smartTree.ActionPresentation; +import com.intellij.ide.util.treeView.smartTree.ActionPresentationData; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author konstantin.aleev + */ +public class ConfigurationTypeGroupingRule implements GroupingRule { + @NonNls private static final String NAME = "TypeGroupingRule"; + + @Override + @NotNull + public String getName() { + return NAME; + } + + @NotNull + @Override + public ActionPresentation getPresentation() { + return new ActionPresentationData(ExecutionBundle.message("runtime.dashboard.group.by.type.action.name"), + ExecutionBundle.message("runtime.dashboard.group.by.type.action.name"), + AllIcons.Actions.GroupByFile); + } + + @Nullable + @Override + public Group getGroup(AbstractTreeNode node) { + if (node instanceof DashboardRunConfigurationNode) { + RunnerAndConfigurationSettings configurationSettings = ((DashboardRunConfigurationNode)node).getConfigurationSettings(); + ConfigurationType type = configurationSettings.getType(); + if (type != null) { + return new GroupImpl<>(type, type.getDisplayName(), type.getIcon()); + } + } + return null; + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/tree/FolderGroupingRule.java b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/FolderGroupingRule.java new file mode 100644 index 000000000000..0ffa88ae58bb --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/FolderGroupingRule.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.tree; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.dashboard.Group; +import com.intellij.execution.dashboard.GroupingRule; +import com.intellij.execution.dashboard.DashboardRunConfigurationNode; +import com.intellij.icons.AllIcons; +import com.intellij.ide.util.treeView.AbstractTreeNode; +import com.intellij.ide.util.treeView.smartTree.ActionPresentation; +import com.intellij.ide.util.treeView.smartTree.ActionPresentationData; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author konstantin.aleev + */ +public class FolderGroupingRule implements GroupingRule { + @NonNls private static final String NAME = "FolderGroupingRule"; + + @Override + @NotNull + public String getName() { + return NAME; + } + + @NotNull + @Override + public ActionPresentation getPresentation() { + return new ActionPresentationData(ExecutionBundle.message("runtime.dashboard.group.by.folder.action.name"), + ExecutionBundle.message("runtime.dashboard.group.by.folder.action.name"), + AllIcons.Actions.GroupByPackage); + } + + @Nullable + @Override + public Group getGroup(AbstractTreeNode node) { + if (node instanceof DashboardRunConfigurationNode) { + RunnerAndConfigurationSettings configurationSettings = ((DashboardRunConfigurationNode)node).getConfigurationSettings(); + String folderName = configurationSettings.getFolderName(); + if (folderName != null) { + return new GroupImpl<>(folderName, folderName, AllIcons.Nodes.Folder); + } + } + return null; + } + +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/tree/GroupImpl.java b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/GroupImpl.java new file mode 100644 index 000000000000..489384409e2a --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/GroupImpl.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.tree; + +import com.intellij.execution.dashboard.Group; + +import javax.swing.*; + +/** + * @author konstantin.aleev + */ +public class GroupImpl implements Group { + private final T myValue; + private final String myName; + private final Icon myIcon; + + public GroupImpl(T value, String name, Icon icon) { + myValue = value; + myName = name; + myIcon = icon; + } + + @Override + public String getName() { + return myName; + } + + @Override + public Icon getIcon() { + return myIcon; + } + + @Override + public int hashCode() { + return myValue.hashCode(); + } + + @Override + public final boolean equals(Object obj) { + if (obj instanceof GroupImpl) { + return myValue.equals(((GroupImpl)obj).myValue); + } + return false; + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/tree/Grouper.java b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/Grouper.java new file mode 100644 index 000000000000..e00916a41f57 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/Grouper.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.tree; + +import com.intellij.execution.dashboard.GroupingRule; +import org.jetbrains.annotations.NotNull; + +/** + * @author konstantin.aleev + */ +public class Grouper { + @NotNull private final GroupingRule myRule; + private boolean myEnabled = true; + + public Grouper(@NotNull GroupingRule rule) { + myRule = rule; + } + + @NotNull + public GroupingRule getRule() { + return myRule; + } + + public boolean isEnabled() { + return myEnabled; + } + + public void setEnabled(boolean enabled) { + myEnabled = enabled; + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/tree/GroupingNode.java b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/GroupingNode.java new file mode 100644 index 000000000000..310988e2d4ab --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/GroupingNode.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.tree; + +import com.intellij.execution.dashboard.DashboardNode; +import com.intellij.execution.dashboard.Group; +import com.intellij.ide.projectView.PresentationData; +import com.intellij.ide.util.treeView.AbstractTreeNode; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author konstantin.aleev + */ +public class GroupingNode extends AbstractTreeNode> implements DashboardNode { + private final List myChildren = new ArrayList<>(); + + public GroupingNode(Project project, Object parent, Group group) { + super(project, Pair.create(parent, group)); + } + + public Group getGroup() { + //noinspection ConstantConditions ??? + return getValue().getSecond(); + } + + @NotNull + @Override + public Collection getChildren() { + return myChildren; + } + + public void addChildren(Collection children) { + myChildren.addAll(children); + } + + @Override + protected void update(PresentationData presentation) { + presentation.setPresentableText(getGroup().getName()); + presentation.setIcon(getGroup().getIcon()); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/tree/RunConfigurationNode.java b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/RunConfigurationNode.java index ecb8fd71eced..d9f0edfd87ad 100644 --- a/platform/lang-impl/src/com/intellij/execution/dashboard/tree/RunConfigurationNode.java +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/RunConfigurationNode.java @@ -77,10 +77,20 @@ class RunConfigurationNode extends AbstractRunConfigurationNode myGroupers; private final RunConfigurationsTreeRootNode myRootElement; - public RuntimeDashboardTreeStructure(@NotNull Project project) { + public RuntimeDashboardTreeStructure(@NotNull Project project, @NotNull List groupers) { super(project); myProject = project; + myGroupers = groupers; myRootElement = new RunConfigurationsTreeRootNode(); } @@ -71,10 +73,13 @@ public class RuntimeDashboardTreeStructure extends AbstractTreeStructureBase { @NotNull @Override public Collection getChildren() { - return RunManager.getInstance(myProject).getAllSettings().stream() - .filter(runConfiguration -> RuntimeDashboardContributor.isShowInDashboard(runConfiguration.getType())) - .map(runConfiguration -> new RunConfigurationNode(myProject, runConfiguration)) - .collect(Collectors.toList()); + return group(myProject, + this, + myGroupers.stream().filter(Grouper::isEnabled).map(Grouper::getRule).collect(Collectors.toList()), + RunManager.getInstance(myProject).getAllSettings().stream() + .filter(runConfiguration -> RuntimeDashboardContributor.isShowInDashboard(runConfiguration.getType())) + .map(runConfiguration -> new RunConfigurationNode(myProject, runConfiguration)) + .collect(Collectors.toList())); } @Override @@ -82,4 +87,33 @@ public class RuntimeDashboardTreeStructure extends AbstractTreeStructureBase { } } + private static Collection group(final Project project, final AbstractTreeNode parent, + List rules, List nodes) { + if (rules.isEmpty()) { + return nodes; + } + final List remaining = new ArrayList<>(rules); + GroupingRule rule = remaining.remove(0); + Map> groups = nodes.stream().collect( + HashMap::new, + (map, node) -> map.computeIfAbsent(rule.getGroup(node), key -> new ArrayList<>()).add(node), + (firstMap, secondMap) -> firstMap.forEach((key, value) -> value.addAll(secondMap.get(key))) + ); + rule.getPermanentGroups().forEach(group -> groups.computeIfAbsent(group, key -> new ArrayList<>())); + final List result = new ArrayList<>(); + final List ungroupedNodes = new ArrayList<>(); + groups.forEach((group, groupedNodes) -> { + if (group == null) { + ungroupedNodes.addAll(group(project, parent, remaining, groupedNodes)); + } else { + GroupingNode node = new GroupingNode(project, parent.getValue(), group); + node.addChildren(group(project, node, remaining, groupedNodes)); + result.add(node); + } + }); + + Collections.sort(result, Comparator.comparing(node -> ((GroupingNode)node).getGroup().getName())); + result.addAll(ungroupedNodes); + return result; + } } diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/tree/StatusGroupingRule.java b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/StatusGroupingRule.java new file mode 100644 index 000000000000..f0544d3de08c --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/tree/StatusGroupingRule.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.dashboard.tree; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.dashboard.Group; +import com.intellij.execution.dashboard.GroupingRule; +import com.intellij.execution.dashboard.DashboardRunConfigurationNode; +import com.intellij.icons.AllIcons; +import com.intellij.ide.util.treeView.AbstractTreeNode; +import com.intellij.ide.util.treeView.smartTree.ActionPresentation; +import com.intellij.ide.util.treeView.smartTree.ActionPresentationData; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author konstantin.aleev + */ +public class StatusGroupingRule implements GroupingRule { + @NonNls private static final String NAME = "StatusGroupingRule"; + + @Override + @NotNull + public String getName() { + return NAME; + } + + @NotNull + @Override + public ActionPresentation getPresentation() { + return new ActionPresentationData(ExecutionBundle.message("runtime.dashboard.group.by.status.action.name"), + ExecutionBundle.message("runtime.dashboard.group.by.status.action.name"), + AllIcons.Actions.GroupByPrefix); // TODO [konstantin.aleev] provide new icon + } + + @NotNull + @Override + public List getPermanentGroups() { + return Arrays.stream(Status.values()).map(Status::getGroup).collect(Collectors.toList()); + } + + @Nullable + @Override + public Group getGroup(AbstractTreeNode node) { + if (node instanceof DashboardRunConfigurationNode) { + if (((DashboardRunConfigurationNode)node).isTerminated()) { + return Status.STOPPED.getGroup(); + } else { + return Status.STARTED.getGroup(); + } + } + return null; + } + + public enum Status { + STARTED(ExecutionBundle.message("runtime.dashboard.started.group.name"), AllIcons.Toolwindows.ToolWindowRun), + STOPPED(ExecutionBundle.message("runtime.dashboard.stopped.group.name"), AllIcons.Actions.Suspend); + + private final String myLabel; + private final Icon myIcon; + + Status(String label, Icon icon) { + myLabel = label; + myIcon = icon; + } + + public Group getGroup() { + return new GroupImpl<>(this, myLabel, myIcon); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java index 00f0c10ae62f..95b734c0592d 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java @@ -218,7 +218,7 @@ public class ExecutionManagerImpl extends ExecutionManager implements Disposable Messages.getQuestionIcon(), option) == Messages.OK; } - private static void stop(@Nullable RunContentDescriptor descriptor) { + public static void stopProcess(@Nullable RunContentDescriptor descriptor) { ProcessHandler processHandler = descriptor != null ? descriptor.getProcessHandler() : null; if (processHandler == null) { return; @@ -502,7 +502,7 @@ public class ExecutionManagerImpl extends ExecutionManager implements Disposable } for (RunContentDescriptor descriptor : runningToStop) { - stop(descriptor); + stopProcess(descriptor); } } diff --git a/platform/platform-resources-en/src/messages/ExecutionBundle.properties b/platform/platform-resources-en/src/messages/ExecutionBundle.properties index dc63cb23d823..957f1691744b 100644 --- a/platform/platform-resources-en/src/messages/ExecutionBundle.properties +++ b/platform/platform-resources-en/src/messages/ExecutionBundle.properties @@ -376,3 +376,16 @@ sm.test.runner.magnitude.assertion.failed.title=Assertion failed sm.test.runner.magnitude.testerror.title=Error runtime.dashboard.empty.selection.message=Select a node in the tree to view details +runtime.dashboard.group.by.type.action.name=Group by Type +runtime.dashboard.group.by.status.action.name=Group by Status +runtime.dashboard.group.by.folder.action.name=Group by Folder +runtime.dashboard.run.action.name=(Re)run +runtime.dashboard.debug.action.name=(Re)run in Debug Mode +runtime.dashboard.stop.action.name=Stop +runtime.dashboard.edit.configuration.action.name=Edit Configuration +runtime.dashboard.edit.configuration.dialog.title=Edit Run Configuration +runtime.dashboard.remove.configuration.action.name=Remove Run Configuration +runtime.dashboard.remove.configuration.dialog.title=Remove Run Configuration +runtime.dashboard.remove.configuration.dialog.message=Are you sure to remove selected run configuration(s)? +runtime.dashboard.started.group.name=Started +runtime.dashboard.stopped.group.name=Stopped diff --git a/platform/platform-resources-en/src/messages/UIBundle.properties b/platform/platform-resources-en/src/messages/UIBundle.properties index e95afdb9740d..289bb44d29f0 100644 --- a/platform/platform-resources-en/src/messages/UIBundle.properties +++ b/platform/platform-resources-en/src/messages/UIBundle.properties @@ -58,7 +58,7 @@ tool.window.name.version.control=Version Control tool.window.name.module.dependencies=Module Dependencies tool.window.name.tasks=Time Tracking tool.window.name.database=Database -tool.window.name.runtime.dashboard=Runtime Dashboard +tool.window.name.runtime.dashboard=Run Dashboard tool.window.move.to.action.group.name=Move to tool.window.move.to.top.action.name=Top tool.window.move.to.left.action.name=Left diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml index be7df8c94155..1f10c983cf3f 100644 --- a/platform/platform-resources/src/idea/LangActions.xml +++ b/platform/platform-resources/src/idea/LangActions.xml @@ -1016,5 +1016,15 @@ + + + + + + + + + + diff --git a/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/ServersToolWindowContent.java b/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/ServersToolWindowContent.java index c96431182352..218b13e671ed 100644 --- a/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/ServersToolWindowContent.java +++ b/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/ServersToolWindowContent.java @@ -1,5 +1,6 @@ package com.intellij.remoteServer.impl.runtime.ui; +import com.intellij.execution.dashboard.TreeContent; import com.intellij.ide.DataManager; import com.intellij.ide.actions.ContextHelpAction; import com.intellij.ide.util.treeView.AbstractTreeNode; @@ -45,7 +46,7 @@ import java.util.Set; /** * @author michael.golubev */ -public class ServersToolWindowContent extends JPanel implements Disposable, ServersTreeNodeSelector { +public class ServersToolWindowContent extends JPanel implements Disposable, ServersTreeNodeSelector, TreeContent { public static final DataKey KEY = DataKey.create("serversToolWindowContent"); @NonNls private static final String PLACE_TOOLBAR = "ServersToolWindowContent#Toolbar"; @NonNls private static final String SERVERS_TOOL_WINDOW_TOOLBAR = "RemoteServersViewToolbar"; @@ -289,6 +290,8 @@ public class ServersToolWindowContent extends JPanel implements Disposable, Serv public void dispose() { } + @Override + @NotNull public TreeBuilderBase getBuilder() { return myBuilder; } diff --git a/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/tree/actions/ServersTreeAction.java b/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/tree/actions/ServersTreeAction.java index 690cfc6bc687..1dee1b5df68e 100644 --- a/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/tree/actions/ServersTreeAction.java +++ b/platform/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/ui/tree/actions/ServersTreeAction.java @@ -1,117 +1,21 @@ package com.intellij.remoteServer.impl.runtime.ui.tree.actions; -import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.execution.dashboard.DashboardTreeAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.DumbAware; import com.intellij.remoteServer.impl.runtime.ui.ServersToolWindowContent; import com.intellij.remoteServer.impl.runtime.ui.tree.ServersTreeNode; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -public abstract class ServersTreeAction extends AnAction implements DumbAware { +public abstract class ServersTreeAction extends DashboardTreeAction + implements DumbAware { protected ServersTreeAction(String text, String description, Icon icon) { super(text, description, icon); } @Override - public void update(@NotNull AnActionEvent e) { - Presentation presentation = e.getPresentation(); - List targetNodes = getTargetNodes(e); - - boolean visible; - boolean enabled; - - if (targetNodes == null) { - visible = false; - enabled = false; - } - else { - visible = true; - enabled = true; - for (T targetNode : targetNodes) { - visible &= isVisible4(targetNode); - enabled &= visible && isEnabled4(targetNode); - } - } - - presentation.setVisible(visible); - presentation.setEnabled(enabled); - updatePresentation(presentation, ContainerUtil.getFirstItem(targetNodes)); - } - - private List getTargetNodes(AnActionEvent e) { - ServersToolWindowContent content = getContent(e); - if (content == null) { - return null; - } - Set selectedElements = content.getBuilder().getSelectedElements(); - int selectionCount = selectedElements.size(); - if (selectionCount == 0 || selectionCount > 1 && !isMultiSelectionAllowed()) { - return null; - } - Class targetNodeClass = getTargetNodeClass(); - List result = new ArrayList<>(); - for (Object selectedElement : selectedElements) { - ServersTreeNode node = (ServersTreeNode)selectedElement; - if (!targetNodeClass.isInstance(node)) { - return null; - } - result.add(targetNodeClass.cast(node)); - } - return result; - } - - private static ServersToolWindowContent getContent(AnActionEvent e) { + protected final ServersToolWindowContent getTreeContent(AnActionEvent e) { return e.getData(ServersToolWindowContent.KEY); } - - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - List targetNodes = getTargetNodes(e); - if (targetNodes == null) { - return; - } - - List verifiedTargetNodes = ContainerUtil.filter(targetNodes, targetNode -> isVisible4(targetNode) && isEnabled4(targetNode)); - doActionPerformed(getContent(e), e, verifiedTargetNodes); - } - - protected boolean isMultiSelectionAllowed() { - return false; - } - - protected boolean isVisible4(T node) { - return true; - } - - protected boolean isEnabled4(T node) { - return true; - } - - protected void updatePresentation(@NotNull Presentation presentation, @Nullable T node) { - } - - protected void doActionPerformed(@NotNull ServersToolWindowContent content, AnActionEvent e, List nodes) { - for (T node : nodes) { - doActionPerformed(content, e, node); - } - } - - protected void doActionPerformed(@NotNull ServersToolWindowContent content, AnActionEvent e, T node) { - doActionPerformed(node); - } - - protected void doActionPerformed(T node) { - throw new UnsupportedOperationException(); - } - - protected abstract Class getTargetNodeClass(); }