blob: 596b3900ba5bb9d04d023b7b67f17f80c8d652c4 [file] [log] [blame]
/*
* Copyright 2000-2014 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.actions;
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.process.ProcessHandler;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.TaskInfo;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListItemDescriptorAdapter;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.ex.StatusBarEx;
import com.intellij.openapi.wm.ex.WindowManagerEx;
import com.intellij.ui.components.JBList;
import com.intellij.ui.popup.list.GroupedItemsListRenderer;
import com.intellij.util.Function;
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.Collections;
import java.util.List;
class StopAction extends DumbAwareAction implements AnAction.TransparentUpdate {
@Override
public void update(final AnActionEvent e) {
boolean enable = false;
Icon icon = getTemplatePresentation().getIcon();
String description = getTemplatePresentation().getDescription();
Presentation presentation = e.getPresentation();
if (ActionPlaces.isMainMenuOrActionSearch(e.getPlace())) {
enable = !getCancellableProcesses(e.getProject()).isEmpty() || !getActiveDescriptors(e.getDataContext()).isEmpty();
presentation.setText(getTemplatePresentation().getText());
}
else {
RunContentDescriptor contentDescriptor = e.getData(LangDataKeys.RUN_CONTENT_DESCRIPTOR);
ProcessHandler processHandler = contentDescriptor == null ? null : contentDescriptor.getProcessHandler();
if (processHandler != null && !processHandler.isProcessTerminated()) {
if (!processHandler.isProcessTerminating()) {
enable = true;
}
else if (processHandler instanceof KillableProcess && ((KillableProcess)processHandler).canKillProcess()) {
enable = true;
icon = AllIcons.Debugger.KillProcess;
description = "Kill process";
}
}
RunProfile runProfile = e.getData(LangDataKeys.RUN_PROFILE);
if (runProfile == null && contentDescriptor == null) {
presentation.setText(getTemplatePresentation().getText());
}
else {
presentation.setText(ExecutionBundle.message("stop.configuration.action.name",
runProfile == null ? contentDescriptor.getDisplayName() : runProfile.getName()));
}
}
presentation.setEnabled(enable);
presentation.setIcon(icon);
presentation.setDescription(description);
}
@Override
public void actionPerformed(final AnActionEvent e) {
final DataContext dataContext = e.getDataContext();
ProcessHandler activeProcessHandler = getHandler(dataContext);
Project project = e.getProject();
List<Pair<TaskInfo, ProgressIndicator>> backgroundTasks = getCancellableProcesses(project);
if (ActionPlaces.isMainMenuOrActionSearch(e.getPlace())) {
if (activeProcessHandler != null && !activeProcessHandler.isProcessTerminating() && !activeProcessHandler.isProcessTerminated()
&& backgroundTasks.isEmpty()) {
stopProcess(activeProcessHandler);
return;
}
Pair<List<HandlerItem>, HandlerItem>
handlerItems = getItemsList(backgroundTasks, getActiveDescriptors(dataContext), activeProcessHandler);
if (handlerItems == null || handlerItems.first.isEmpty()) {
return;
}
final JBList list = new JBList(handlerItems.first);
if (handlerItems.second != null) list.setSelectedValue(handlerItems.second, true);
list.setCellRenderer(new GroupedItemsListRenderer(new ListItemDescriptorAdapter() {
@Nullable
@Override
public String getTextFor(Object value) {
return value instanceof HandlerItem ? ((HandlerItem)value).displayName : null;
}
@Nullable
@Override
public Icon getIconFor(Object value) {
return value instanceof HandlerItem ? ((HandlerItem)value).icon : null;
}
@Override
public boolean hasSeparatorAboveOf(Object value) {
return value instanceof HandlerItem && ((HandlerItem)value).hasSeparator;
}
}));
final PopupChooserBuilder builder = JBPopupFactory.getInstance().createListPopupBuilder(list);
final JBPopup popup = builder
.setMovable(true)
.setTitle(handlerItems.first.size() == 1 ? "Confirm process stop" : "Stop process")
.setFilteringEnabled(new Function<Object, String>() {
@Override
public String fun(Object o) {
return ((HandlerItem)o).displayName;
}
})
.setItemChoosenCallback(new Runnable() {
@Override
public void run() {
HandlerItem item = (HandlerItem)list.getSelectedValue();
if (item != null) item.stop();
}
}).setRequestFocus(true).createPopup();
assert project != null;
popup.showCenteredInCurrentWindow(project);
}
else {
if (activeProcessHandler != null) {
stopProcess(activeProcessHandler);
}
}
}
private static List<Pair<TaskInfo, ProgressIndicator>> getCancellableProcesses(Project project) {
IdeFrame frame = ((WindowManagerEx)WindowManager.getInstance()).findFrameFor(project);
StatusBarEx statusBar = frame == null ? null : (StatusBarEx)frame.getStatusBar();
if (statusBar == null) return Collections.emptyList();
return ContainerUtil.findAll(statusBar.getBackgroundProcesses(),
new Condition<Pair<TaskInfo, ProgressIndicator>>() {
@Override
public boolean value(Pair<TaskInfo, ProgressIndicator> pair) {
return pair.first.isCancellable() && !pair.second.isCanceled();
}
});
}
@Nullable
private static Pair<List<HandlerItem>, HandlerItem> getItemsList(List<Pair<TaskInfo, ProgressIndicator>> tasks,
List<RunContentDescriptor> descriptors,
ProcessHandler activeProcessHandler) {
if (tasks.isEmpty() && descriptors.isEmpty()) {
return null;
}
List<HandlerItem> items = new ArrayList<HandlerItem>(tasks.size() + descriptors.size());
HandlerItem selected = null;
for (RunContentDescriptor descriptor : descriptors) {
final ProcessHandler handler = descriptor.getProcessHandler();
if (handler != null) {
HandlerItem item = new HandlerItem(descriptor.getDisplayName(), descriptor.getIcon(), false) {
@Override
void stop() {
stopProcess(handler);
}
};
items.add(item);
if (handler == activeProcessHandler) {
selected = item;
}
}
}
boolean hasSeparator = true;
for (final Pair<TaskInfo, ProgressIndicator> eachPair : tasks) {
items.add(new HandlerItem(eachPair.first.getTitle(), AllIcons.Process.Step_passive, hasSeparator) {
@Override
void stop() {
eachPair.second.cancel();
}
});
hasSeparator = false;
}
return Pair.create(items, selected);
}
private static void stopProcess(ProcessHandler processHandler) {
if (processHandler instanceof KillableProcess && processHandler.isProcessTerminating()) {
((KillableProcess)processHandler).killProcess();
return;
}
if (processHandler.detachIsDefault()) {
processHandler.detachProcess();
}
else {
processHandler.destroyProcess();
}
}
@Nullable
static ProcessHandler getHandler(@NotNull DataContext dataContext) {
final RunContentDescriptor contentDescriptor = LangDataKeys.RUN_CONTENT_DESCRIPTOR.getData(dataContext);
if (contentDescriptor != null) {
// toolwindow case
return contentDescriptor.getProcessHandler();
}
else {
// main menu toolbar
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
final RunContentDescriptor selectedContent =
project == null ? null : ExecutionManager.getInstance(project).getContentManager().getSelectedContent();
return selectedContent == null ? null : selectedContent.getProcessHandler();
}
}
@NotNull
private static List<RunContentDescriptor> getActiveDescriptors(final DataContext dataContext) {
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project == null) {
return Collections.emptyList();
}
final List<RunContentDescriptor> runningProcesses = ExecutionManager.getInstance(project).getContentManager().getAllDescriptors();
if (runningProcesses.isEmpty()) {
return Collections.emptyList();
}
final List<RunContentDescriptor> activeDescriptors = new ArrayList<RunContentDescriptor>();
for (RunContentDescriptor descriptor : runningProcesses) {
final ProcessHandler processHandler = descriptor.getProcessHandler();
if (processHandler != null && !processHandler.isProcessTerminating() && !processHandler.isProcessTerminated()) {
activeDescriptors.add(descriptor);
}
}
return activeDescriptors;
}
private abstract static class HandlerItem {
final String displayName;
final Icon icon;
final boolean hasSeparator;
private HandlerItem(String displayName, Icon icon, boolean hasSeparator) {
this.displayName = displayName;
this.icon = icon;
this.hasSeparator = hasSeparator;
}
public String toString() {
return displayName;
}
abstract void stop();
}
}