/* * Copyright 2000-2016 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.*; import com.intellij.execution.configurations.RunConfiguration; import com.intellij.execution.dashboard.tree.DashboardGrouper; import com.intellij.execution.impl.ExecutionManagerImpl; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.icons.AllIcons; 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.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.registry.Registry; 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.ui.content.Content; import com.intellij.ui.content.ContentFactory; import com.intellij.ui.content.ContentManager; import com.intellij.ui.content.ContentUI; import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * @author konstantin.aleev */ @State(name = "RunDashboard", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) public class RunDashboardManagerImpl implements RunDashboardManager, PersistentStateComponent<RunDashboardManagerImpl.State> { @NotNull private final Project myProject; @NotNull private final ContentManager myContentManager; @NotNull private final List<DashboardGrouper> myGroupers; private RunDashboardContent myDashboardContent; public RunDashboardManagerImpl(@NotNull final Project project) { myProject = project; ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); ContentUI contentUI = new PanelContentUI(); myContentManager = contentFactory.createContentManager(contentUI, false, project); myGroupers = Arrays.stream(DashboardGroupingRule.EP_NAME.getExtensions()).sorted(DashboardGroupingRule.PRIORITY_COMPARATOR).map(DashboardGrouper::new) .collect(Collectors.toList()); if (isDashboardEnabled()) { initToolWindowListeners(); } } private static boolean isDashboardEnabled() { return Registry.is("ide.run.dashboard") && RunDashboardContributor.EP_NAME.getExtensions().length > 0; } private void initToolWindowListeners() { RunManagerEx.getInstanceEx(myProject).addRunManagerListener(new RunManagerListener() { @Override public void runConfigurationAdded(@NotNull RunnerAndConfigurationSettings settings) { updateDashboardIfNeeded(settings); } @Override public void runConfigurationRemoved(@NotNull RunnerAndConfigurationSettings settings) { updateDashboardIfNeeded(settings); } @Override public void runConfigurationChanged(@NotNull RunnerAndConfigurationSettings settings) { updateDashboardIfNeeded(settings); } }); MessageBusConnection connection = myProject.getMessageBus().connect(myProject); connection.subscribe(ExecutionManager.EXECUTION_TOPIC, new ExecutionListener() { @Override public void processStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env, final @NotNull ProcessHandler handler) { updateDashboardIfNeeded(env.getRunnerAndConfigurationSettings()); } @Override public void processTerminated(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler, int exitCode) { updateDashboardIfNeeded(env.getRunnerAndConfigurationSettings()); } }); connection.subscribe(RunDashboardManager.DASHBOARD_TOPIC, new DashboardListener() { @Override public void contentChanged(boolean withStructure) { updateDashboard(withStructure); } }); connection.subscribe(DumbService.DUMB_MODE, new DumbService.DumbModeListener() { @Override public void enteredDumbMode() { } @Override public void exitDumbMode() { updateDashboard(false); } }); } @Override public ContentManager getDashboardContentManager() { return myContentManager; } @Override public String getToolWindowId() { return ToolWindowId.RUN_DASHBOARD; } @Override public Icon getToolWindowIcon() { return AllIcons.Toolwindows.ToolWindowRun; // TODO [konstantin.aleev] provide new icon } @Override public boolean isToolWindowAvailable() { return isDashboardEnabled() && hasContent(); } @Override public void createToolWindowContent(@NotNull ToolWindow toolWindow) { myDashboardContent = new RunDashboardContent(myProject, myContentManager, myGroupers); ContentManager contentManager = toolWindow.getContentManager(); Content content = contentManager.getFactory().createContent(myDashboardContent, null, false); Disposer.register(content, myDashboardContent); Disposer.register(content, () -> myDashboardContent = null); toolWindow.getContentManager().addContent(content); } @Override public List<Pair<RunnerAndConfigurationSettings, RunContentDescriptor>> getRunConfigurations() { List<Pair<RunnerAndConfigurationSettings, RunContentDescriptor>> result = new ArrayList<>(); List<RunnerAndConfigurationSettings> configurations = RunManager.getInstance(myProject).getAllSettings().stream() .filter(runConfiguration -> RunDashboardContributor.isShowInDashboard(runConfiguration.getType())).collect(Collectors.toList()); //noinspection ConstantConditions ??? ExecutionManagerImpl executionManager = ExecutionManagerImpl.getInstance(myProject); configurations.forEach(configurationSettings -> { List<RunContentDescriptor> descriptors = executionManager.getDescriptors(settings -> Comparing.equal(settings.getConfiguration(), configurationSettings.getConfiguration())); if (descriptors.isEmpty()) { result.add(Pair.create(configurationSettings, null)); } else { descriptors.forEach(descriptor -> result.add(Pair.create(configurationSettings, descriptor))); } }); // It is possible that run configuration was deleted, but there is running content descriptor for such run configuration. // It should be shown in he dashboard tree. List<RunConfiguration> storedConfigurations = configurations.stream().map(RunnerAndConfigurationSettings::getConfiguration).collect(Collectors.toList()); List<RunContentDescriptor> notStoredDescriptors = executionManager.getRunningDescriptors(settings -> { RunConfiguration configuration = settings.getConfiguration(); return RunDashboardContributor.isShowInDashboard(settings.getType()) && !storedConfigurations.contains(configuration); }); notStoredDescriptors.forEach(descriptor -> { Set<RunnerAndConfigurationSettings> settings = executionManager.getConfigurations(descriptor); settings.forEach(setting -> result.add(Pair.create(setting, descriptor))); }); return result; } private void updateDashboardIfNeeded(@Nullable RunnerAndConfigurationSettings settings) { if (settings != null && RunDashboardContributor.isShowInDashboard(settings.getType())) { updateDashboard(true); } } private void updateDashboard(final boolean withStructure) { final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject); toolWindowManager.invokeLater(() -> { if (myProject.isDisposed()) { return; } if (withStructure) { boolean available = hasContent(); ToolWindow toolWindow = toolWindowManager.getToolWindow(getToolWindowId()); if (toolWindow == null) { if (available) { createToolWindow(); } return; } boolean doShow = !toolWindow.isAvailable() && available; toolWindow.setAvailable(available, null); if (doShow) { toolWindow.show(null); } } if (myDashboardContent != null) { myDashboardContent.updateContent(withStructure); } }); } private void createToolWindow() { ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject); ToolWindow toolWindow = toolWindowManager.registerToolWindow(getToolWindowId(), false, ToolWindowAnchor.BOTTOM, myProject, true); toolWindow.setIcon(getToolWindowIcon()); createToolWindowContent(toolWindow); } private boolean hasContent() { return !getRunConfigurations().isEmpty(); } @Nullable @Override public State getState() { State state = new State(); state.ruleStates = myGroupers.stream().filter(grouper -> !grouper.getRule().isAlwaysEnabled()) .map(grouper -> new RuleState(grouper.getRule().getName(), grouper.isEnabled())).collect(Collectors.toList()); return state; } @Override public void loadState(State state) { state.ruleStates.forEach(ruleState -> { for (DashboardGrouper grouper : myGroupers) { if (grouper.getRule().getName().equals(ruleState.name) && !grouper.getRule().isAlwaysEnabled()) { grouper.setEnabled(ruleState.enabled); return; } } }); } static class State { public List<RuleState> ruleStates = new ArrayList<>(); } private static class RuleState { public String name; public boolean enabled = true; @SuppressWarnings("UnusedDeclaration") public RuleState() { } public RuleState(String name, boolean enabled) { this.name = name; this.enabled = enabled; } } }