blob: c9dae095522c547ebbed676990465b367c5157f4 [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.ui;
import com.intellij.execution.*;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.GenericProgramRunner;
import com.intellij.execution.ui.layout.impl.DockableGridContainerFactory;
import com.intellij.ide.DataManager;
import com.intellij.ide.impl.ContentManagerWatcher;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.impl.LaterInvocator;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerListener;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter;
import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
import com.intellij.ui.AppUIUtil;
import com.intellij.ui.content.*;
import com.intellij.ui.docking.DockManager;
import com.intellij.util.SmartList;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
public class RunContentManagerImpl implements RunContentManager, Disposable {
public static final Key<Boolean> ALWAYS_USE_DEFAULT_STOPPING_BEHAVIOUR_KEY = Key.create("ALWAYS_USE_DEFAULT_STOPPING_BEHAVIOUR_KEY");
private static final Logger LOG = Logger.getInstance(RunContentManagerImpl.class);
private static final Key<RunContentDescriptor> DESCRIPTOR_KEY = Key.create("Descriptor");
private final Project myProject;
private final Map<String, ContentManager> myToolwindowIdToContentManagerMap = new THashMap<String, ContentManager>();
private final Map<RunContentListener, Disposable> myListeners = new THashMap<RunContentListener, Disposable>();
private final LinkedList<String> myToolwindowIdZBuffer = new LinkedList<String>();
public RunContentManagerImpl(@NotNull Project project, @NotNull DockManager dockManager) {
myProject = project;
DockableGridContainerFactory containerFactory = new DockableGridContainerFactory();
dockManager.register(DockableGridContainerFactory.TYPE, containerFactory);
Disposer.register(myProject, containerFactory);
AppUIUtil.invokeOnEdt(new Runnable() {
@Override
public void run() {
init();
}
}, myProject.getDisposed());
}
// must be called on EDT
private void init() {
ToolWindowManagerEx toolWindowManager = ToolWindowManagerEx.getInstanceEx(myProject);
if (toolWindowManager == null) {
return;
}
for (Executor executor : ExecutorRegistry.getInstance().getRegisteredExecutors()) {
registerToolwindow(executor, toolWindowManager);
}
toolWindowManager.addToolWindowManagerListener(new ToolWindowManagerAdapter() {
@Override
public void stateChanged() {
if (myProject.isDisposed()) {
return;
}
ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
Set<String> currentWindows = new THashSet<String>();
ContainerUtil.addAll(currentWindows, toolWindowManager.getToolWindowIds());
myToolwindowIdZBuffer.retainAll(currentWindows);
final String activeToolWindowId = toolWindowManager.getActiveToolWindowId();
if (activeToolWindowId != null) {
if (myToolwindowIdZBuffer.remove(activeToolWindowId)) {
myToolwindowIdZBuffer.addFirst(activeToolWindowId);
}
}
}
});
}
@Override
public void dispose() {
}
private void registerToolwindow(@NotNull final Executor executor, @NotNull ToolWindowManagerEx toolWindowManager) {
final String toolWindowId = executor.getToolWindowId();
if (toolWindowManager.getToolWindow(toolWindowId) != null) {
return;
}
ToolWindow toolWindow = toolWindowManager.registerToolWindow(toolWindowId, true, ToolWindowAnchor.BOTTOM, this, true);
final ContentManager contentManager = toolWindow.getContentManager();
contentManager.addDataProvider(new DataProvider() {
private int myInsideGetData = 0;
@Override
public Object getData(String dataId) {
myInsideGetData++;
try {
if (PlatformDataKeys.HELP_ID.is(dataId)) {
return executor.getHelpId();
}
else {
return myInsideGetData == 1 ? DataManager.getInstance().getDataContext(contentManager.getComponent()).getData(dataId) : null;
}
}
finally {
myInsideGetData--;
}
}
});
toolWindow.setIcon(executor.getToolWindowIcon());
new ContentManagerWatcher(toolWindow, contentManager);
contentManager.addContentManagerListener(new ContentManagerAdapter() {
@Override
public void selectionChanged(final ContentManagerEvent event) {
Content content = event.getContent();
getSyncPublisher().contentSelected(content == null ? null : getRunContentDescriptorByContent(content), executor);
}
});
myToolwindowIdToContentManagerMap.put(toolWindowId, contentManager);
Disposer.register(contentManager, new Disposable() {
@Override
public void dispose() {
myToolwindowIdToContentManagerMap.remove(toolWindowId).removeAllContents(true);
myToolwindowIdZBuffer.remove(toolWindowId);
}
});
myToolwindowIdZBuffer.addLast(toolWindowId);
}
private RunContentWithExecutorListener getSyncPublisher() {
return myProject.getMessageBus().syncPublisher(TOPIC);
}
@Override
public void toFrontRunContent(final Executor requestor, final ProcessHandler handler) {
final RunContentDescriptor descriptor = getDescriptorBy(handler, requestor);
if (descriptor == null) {
return;
}
toFrontRunContent(requestor, descriptor);
}
@Override
public void toFrontRunContent(final Executor requestor, final RunContentDescriptor descriptor) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
ContentManager contentManager = getContentManagerForRunner(requestor);
Content content = getRunContentByDescriptor(contentManager, descriptor);
if (content != null) {
contentManager.setSelectedContent(content);
ToolWindowManager.getInstance(myProject).getToolWindow(requestor.getToolWindowId()).show(null);
}
}
}, myProject.getDisposed());
}
@Override
public void hideRunContent(@NotNull final Executor executor, final RunContentDescriptor descriptor) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(executor.getToolWindowId());
if (toolWindow != null) {
toolWindow.hide(null);
}
}
}, myProject.getDisposed());
}
@Override
@Nullable
public RunContentDescriptor getSelectedContent(final Executor executor) {
final Content selectedContent = getContentManagerForRunner(executor).getSelectedContent();
return selectedContent != null ? getRunContentDescriptorByContent(selectedContent) : null;
}
@Override
@Nullable
public RunContentDescriptor getSelectedContent() {
for (String activeWindow : myToolwindowIdZBuffer) {
final ContentManager contentManager = myToolwindowIdToContentManagerMap.get(activeWindow);
if (contentManager == null) {
continue;
}
final Content selectedContent = contentManager.getSelectedContent();
if (selectedContent == null) {
if (contentManager.getContentCount() == 0) {
// continue to the next window if the content manager is empty
continue;
}
else {
// stop iteration over windows because there is some content in the window and the window is the last used one
break;
}
}
// here we have selected content
return getRunContentDescriptorByContent(selectedContent);
}
return null;
}
@Override
public boolean removeRunContent(@NotNull final Executor executor, final RunContentDescriptor descriptor) {
final ContentManager contentManager = getContentManagerForRunner(executor);
final Content content = getRunContentByDescriptor(contentManager, descriptor);
return content != null && contentManager.removeContent(content, true);
}
@Override
public void showRunContent(@NotNull Executor executor, @NotNull RunContentDescriptor descriptor) {
showRunContent(executor, descriptor, descriptor.getExecutionId());
}
public void showRunContent(@NotNull final Executor executor, @NotNull final RunContentDescriptor descriptor, long executionId) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return;
}
final ContentManager contentManager = getContentManagerForRunner(executor);
RunContentDescriptor oldDescriptor = chooseReuseContentForDescriptor(contentManager, descriptor, executionId, descriptor.getDisplayName());
final Content content;
if (oldDescriptor == null) {
content = createNewContent(contentManager, descriptor, executor);
Icon icon = descriptor.getIcon();
content.setIcon(icon == null ? executor.getToolWindowIcon() : icon);
}
else {
content = oldDescriptor.getAttachedContent();
LOG.assertTrue(content != null);
getSyncPublisher().contentRemoved(oldDescriptor, executor);
Disposer.dispose(oldDescriptor); // is of the same category, can be reused
}
content.setExecutionId(executionId);
content.setComponent(descriptor.getComponent());
content.setPreferredFocusedComponent(descriptor.getPreferredFocusComputable());
content.putUserData(DESCRIPTOR_KEY, descriptor);
final ProcessHandler processHandler = descriptor.getProcessHandler();
if (processHandler != null) {
final ProcessAdapter processAdapter = new ProcessAdapter() {
@Override
public void startNotified(final ProcessEvent event) {
LaterInvocator.invokeLater(new Runnable() {
@Override
public void run() {
final Icon icon = descriptor.getIcon();
content.setIcon(icon == null ? executor.getToolWindowIcon() : icon);
}
});
}
@Override
public void processTerminated(final ProcessEvent event) {
LaterInvocator.invokeLater(new Runnable() {
@Override
public void run() {
final Icon icon = descriptor.getIcon();
content.setIcon(icon == null ? executor.getDisabledIcon() : IconLoader.getTransparentIcon(icon));
}
});
}
};
processHandler.addProcessListener(processAdapter);
final Disposable disposer = content.getDisposer();
if (disposer != null) {
Disposer.register(disposer, new Disposable() {
@Override
public void dispose() {
processHandler.removeProcessListener(processAdapter);
}
});
}
}
content.setDisplayName(descriptor.getDisplayName());
descriptor.setAttachedContent(content);
content.getManager().setSelectedContent(content);
if (!descriptor.isActivateToolWindowWhenAdded()) {
return;
}
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
ToolWindow window = ToolWindowManager.getInstance(myProject).getToolWindow(executor.getToolWindowId());
// let's activate tool window, but don't move focus
//
// window.show() isn't valid here, because it will not
// mark the window as "last activated" windows and thus
// some action like navigation up/down in stacktrace wont
// work correctly
descriptor.getPreferredFocusComputable();
window.activate(null, descriptor.isAutoFocusContent(), descriptor.isAutoFocusContent());
}
}, myProject.getDisposed());
}
@Override
@Nullable
@Deprecated
public RunContentDescriptor getReuseContent(final Executor requestor, DataContext dataContext) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return null;
}
//noinspection deprecation
return getReuseContent(requestor, GenericProgramRunner.CONTENT_TO_REUSE_DATA_KEY.getData(dataContext));
}
@Override
@Nullable
@Deprecated
public RunContentDescriptor getReuseContent(Executor requestor, @Nullable RunContentDescriptor contentToReuse) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return null;
}
if (contentToReuse != null) {
return contentToReuse;
}
return chooseReuseContentForDescriptor(getContentManagerForRunner(requestor), null, 0L, null);
}
@Nullable
@Override
public RunContentDescriptor getReuseContent(Executor requestor, @NotNull ExecutionEnvironment executionEnvironment) {
return getReuseContent(executionEnvironment);
}
@Nullable
@Override
public RunContentDescriptor getReuseContent(@NotNull ExecutionEnvironment executionEnvironment) {
if (ApplicationManager.getApplication().isUnitTestMode()) return null;
RunContentDescriptor contentToReuse = executionEnvironment.getContentToReuse();
if (contentToReuse != null) {
return contentToReuse;
}
final ContentManager contentManager = getContentManagerForRunner(executionEnvironment.getExecutor());
return chooseReuseContentForDescriptor(contentManager, null, executionEnvironment.getExecutionId(),
executionEnvironment.toString());
}
@Override
public RunContentDescriptor findContentDescriptor(final Executor requestor, final ProcessHandler handler) {
return getDescriptorBy(handler, requestor);
}
@Override
public void showRunContent(@NotNull Executor info, @NotNull RunContentDescriptor descriptor, @Nullable RunContentDescriptor contentToReuse) {
copyContentAndBehavior(descriptor, contentToReuse);
showRunContent(info, descriptor, descriptor.getExecutionId());
}
public static void copyContentAndBehavior(@NotNull RunContentDescriptor descriptor, @Nullable RunContentDescriptor contentToReuse) {
if (contentToReuse != null) {
Content attachedContent = contentToReuse.getAttachedContent();
if (attachedContent != null && attachedContent.isValid()) {
descriptor.setAttachedContent(attachedContent);
}
if (contentToReuse.isReuseToolWindowActivation()) {
descriptor.setActivateToolWindowWhenAdded(contentToReuse.isActivateToolWindowWhenAdded());
}
}
}
@Nullable
private static RunContentDescriptor chooseReuseContentForDescriptor(@NotNull ContentManager contentManager,
@Nullable RunContentDescriptor descriptor,
long executionId,
@Nullable String preferredName) {
Content content = null;
if (descriptor != null) {
//Stage one: some specific descriptors (like AnalyzeStacktrace) cannot be reused at all
if (descriptor.isContentReuseProhibited()) {
return null;
}
//Stage two: try to get content from descriptor itself
final Content attachedContent = descriptor.getAttachedContent();
if (attachedContent != null && attachedContent.isValid() && contentManager.getIndexOfContent(attachedContent) != -1) {
content = attachedContent;
}
}
//Stage three: choose the content with name we prefer
if (content == null) {
content = getContentFromManager(contentManager, preferredName, executionId);
}
if (content == null || !isTerminated(content) || (content.getExecutionId() == executionId && executionId != 0)) {
return null;
}
final RunContentDescriptor oldDescriptor = getRunContentDescriptorByContent(content);
if (oldDescriptor != null && !oldDescriptor.isContentReuseProhibited() ) {
//content.setExecutionId(executionId);
return oldDescriptor;
}
return null;
}
@Nullable
private static Content getContentFromManager(ContentManager contentManager, @Nullable String preferredName, long executionId) {
ArrayList<Content> contents = new ArrayList<Content>(Arrays.asList(contentManager.getContents()));
Content first = contentManager.getSelectedContent();
if (first != null && contents.remove(first)) {//selected content should be checked first
contents.add(0, first);
}
if (preferredName != null) {//try to match content with specified preferred name
for (Content c : contents) {
if (canReuseContent(c, executionId) && preferredName.equals(c.getDisplayName())) {
return c;
}
}
}
for (Content c : contents) {//return first "good" content
if (canReuseContent(c, executionId)) {
return c;
}
}
return null;
}
private static boolean canReuseContent(Content c, long executionId) {
return c != null && !c.isPinned() && isTerminated(c) && !(c.getExecutionId() == executionId && executionId != 0);
}
@NotNull
private ContentManager getContentManagerForRunner(final Executor executor) {
final ContentManager contentManager = myToolwindowIdToContentManagerMap.get(executor.getToolWindowId());
if (contentManager == null) {
LOG.error("Runner " + executor.getId() + " is not registered");
}
//noinspection ConstantConditions
return contentManager;
}
private Content createNewContent(final ContentManager contentManager, final RunContentDescriptor descriptor, Executor executor) {
final String processDisplayName = descriptor.getDisplayName();
final Content content = ContentFactory.SERVICE.getInstance().createContent(descriptor.getComponent(), processDisplayName, true);
content.putUserData(DESCRIPTOR_KEY, descriptor);
content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
contentManager.addContent(content);
new CloseListener(content, executor);
return content;
}
private static boolean isTerminated(@NotNull Content content) {
RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
ProcessHandler processHandler = descriptor == null ? null : descriptor.getProcessHandler();
return processHandler == null || processHandler.isProcessTerminated();
}
@Nullable
private static RunContentDescriptor getRunContentDescriptorByContent(@NotNull Content content) {
return content.getUserData(DESCRIPTOR_KEY);
}
@Override
@Nullable
public ToolWindow getToolWindowByDescriptor(@NotNull RunContentDescriptor descriptor) {
for (Map.Entry<String, ContentManager> entry : myToolwindowIdToContentManagerMap.entrySet()) {
if (getRunContentByDescriptor(entry.getValue(), descriptor) != null) {
return ToolWindowManager.getInstance(myProject).getToolWindow(entry.getKey());
}
}
return null;
}
@Nullable
private static Content getRunContentByDescriptor(@NotNull ContentManager contentManager, @NotNull RunContentDescriptor descriptor) {
for (Content content : contentManager.getContents()) {
if (descriptor.equals(content.getUserData(DESCRIPTOR_KEY))) {
return content;
}
}
return null;
}
@Override
public void addRunContentListener(@NotNull final RunContentListener listener, final Executor executor) {
final Disposable disposable = Disposer.newDisposable();
myProject.getMessageBus().connect(disposable).subscribe(TOPIC, new RunContentWithExecutorListener() {
@Override
public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor2) {
if (executor2.equals(executor)) {
listener.contentSelected(descriptor);
}
}
@Override
public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor2) {
if (executor2.equals(executor)) {
listener.contentRemoved(descriptor);
}
}
});
myListeners.put(listener, disposable);
}
@Override
public void addRunContentListener(@NotNull final RunContentListener listener) {
final Disposable disposable = Disposer.newDisposable();
myProject.getMessageBus().connect(disposable).subscribe(TOPIC, new RunContentWithExecutorListener() {
@Override
public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) {
listener.contentSelected(descriptor);
}
@Override
public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) {
listener.contentRemoved(descriptor);
}
});
myListeners.put(listener, disposable);
}
@Override
@NotNull
public List<RunContentDescriptor> getAllDescriptors() {
if (myToolwindowIdToContentManagerMap.isEmpty()) {
return Collections.emptyList();
}
List<RunContentDescriptor> descriptors = new SmartList<RunContentDescriptor>();
for (String id : myToolwindowIdToContentManagerMap.keySet()) {
for (Content content : myToolwindowIdToContentManagerMap.get(id).getContents()) {
RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
if (descriptor != null) {
descriptors.add(descriptor);
}
}
}
return descriptors;
}
@Override
public void removeRunContentListener(final RunContentListener listener) {
Disposable disposable = myListeners.remove(listener);
if (disposable != null) {
Disposer.dispose(disposable);
}
}
@Nullable
private RunContentDescriptor getDescriptorBy(ProcessHandler handler, Executor runnerInfo) {
for (Content content : getContentManagerForRunner(runnerInfo).getContents()) {
RunContentDescriptor runContentDescriptor = getRunContentDescriptorByContent(content);
assert runContentDescriptor != null;
if (runContentDescriptor.getProcessHandler() == handler) {
return runContentDescriptor;
}
}
return null;
}
private class CloseListener extends ContentManagerAdapter implements ProjectManagerListener {
private Content myContent;
private final Executor myExecutor;
private CloseListener(@NotNull final Content content, @NotNull Executor executor) {
myContent = content;
content.getManager().addContentManagerListener(this);
ProjectManager.getInstance().addProjectManagerListener(this);
myExecutor = executor;
}
@Override
public void contentRemoved(final ContentManagerEvent event) {
final Content content = event.getContent();
if (content == myContent) {
dispose();
}
}
private void dispose() {
if (myContent == null) return;
final Content content = myContent;
try {
RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
getSyncPublisher().contentRemoved(descriptor, myExecutor);
if (descriptor != null) {
Disposer.dispose(descriptor);
}
}
finally {
content.getManager().removeContentManagerListener(this);
ProjectManager.getInstance().removeProjectManagerListener(this);
content.release(); // don't invoke myContent.release() because myContent becomes null after destroyProcess()
myContent = null;
}
}
@Override
public void contentRemoveQuery(final ContentManagerEvent event) {
if (event.getContent() == myContent) {
final boolean canClose = closeQuery(false);
if (!canClose) {
event.consume();
}
}
}
@Override
public void projectOpened(final Project project) {
}
@Override
public void projectClosed(final Project project) {
if (myContent != null && project == myProject) {
myContent.getManager().removeContent(myContent, true);
dispose(); // Dispose content even if content manager refused to.
}
}
@Override
public boolean canCloseProject(final Project project) {
if (project != myProject) return true;
if (myContent == null) return true;
final boolean canClose = closeQuery(true);
if (canClose) {
myContent.getManager().removeContent(myContent, true);
myContent = null;
}
return canClose;
}
@Override
public void projectClosing(final Project project) {
}
private boolean closeQuery(boolean modal) {
final RunContentDescriptor descriptor = getRunContentDescriptorByContent(myContent);
if (descriptor == null) {
return true;
}
final ProcessHandler processHandler = descriptor.getProcessHandler();
if (processHandler == null || processHandler.isProcessTerminated() || processHandler.isProcessTerminating()) {
return true;
}
final boolean destroyProcess;
//noinspection deprecation
if (processHandler.isSilentlyDestroyOnClose() || Boolean.TRUE.equals(processHandler.getUserData(ProcessHandler.SILENTLY_DESTROY_ON_CLOSE))) {
destroyProcess = true;
}
else {
//todo[nik] this is a temporary solution for the following problem: some configurations should not allow user to choose between 'terminating' and 'detaching'
final boolean useDefault = Boolean.TRUE.equals(processHandler.getUserData(ALWAYS_USE_DEFAULT_STOPPING_BEHAVIOUR_KEY));
final TerminateRemoteProcessDialog.TerminateOption option = new TerminateRemoteProcessDialog.TerminateOption(processHandler.detachIsDefault(), useDefault);
final int rc = TerminateRemoteProcessDialog.show(myProject, descriptor.getDisplayName(), option);
if (rc != DialogWrapper.OK_EXIT_CODE) return false;
destroyProcess = !option.isToBeShown();
}
if (destroyProcess) {
processHandler.destroyProcess();
}
else {
processHandler.detachProcess();
}
waitForProcess(descriptor, modal);
return true;
}
}
private void waitForProcess(final RunContentDescriptor descriptor, final boolean modal) {
final ProcessHandler processHandler = descriptor.getProcessHandler();
final boolean killable = !modal && (processHandler instanceof KillableProcess) && ((KillableProcess)processHandler).canKillProcess();
String title = ExecutionBundle.message("terminating.process.progress.title", descriptor.getDisplayName());
ProgressManager.getInstance().run(new Task.Backgroundable(myProject, title, true) {
{
if (killable) {
String cancelText= ExecutionBundle.message("terminating.process.progress.kill");
setCancelText(cancelText);
setCancelTooltipText(cancelText);
}
}
@Override
public boolean isConditionalModal() {
return modal;
}
@Override
public boolean shouldStartInBackground() {
return !modal;
}
@Override
public void run(@NotNull final ProgressIndicator progressIndicator) {
final Semaphore semaphore = new Semaphore();
semaphore.down();
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
final ProcessHandler processHandler = descriptor.getProcessHandler();
try {
if (processHandler != null) {
processHandler.waitFor();
}
}
finally {
semaphore.up();
}
}
});
progressIndicator.setText(ExecutionBundle.message("waiting.for.vm.detach.progress.text"));
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
while (true) {
if (progressIndicator.isCanceled() || !progressIndicator.isRunning()) {
semaphore.up();
break;
}
try {
//noinspection SynchronizeOnThis
synchronized (this) {
//noinspection SynchronizeOnThis
wait(2000L);
}
}
catch (InterruptedException ignore) {
}
}
}
});
semaphore.waitFor();
}
@Override
public void onCancel() {
if (killable && !processHandler.isProcessTerminated()) {
((KillableProcess)processHandler).killProcess();
}
}
});
}
}