2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.execution.ui;
18 import com.intellij.execution.*;
19 import com.intellij.execution.process.ProcessAdapter;
20 import com.intellij.execution.process.ProcessEvent;
21 import com.intellij.execution.process.ProcessHandler;
22 import com.intellij.execution.runners.ExecutionEnvironment;
23 import com.intellij.execution.runners.GenericProgramRunner;
24 import com.intellij.execution.ui.layout.impl.DockableGridContainerFactory;
25 import com.intellij.ide.DataManager;
26 import com.intellij.ide.impl.ContentManagerWatcher;
27 import com.intellij.openapi.Disposable;
28 import com.intellij.openapi.actionSystem.DataContext;
29 import com.intellij.openapi.actionSystem.DataProvider;
30 import com.intellij.openapi.actionSystem.PlatformDataKeys;
31 import com.intellij.openapi.application.ApplicationManager;
32 import com.intellij.openapi.diagnostic.Logger;
33 import com.intellij.openapi.progress.ProgressIndicator;
34 import com.intellij.openapi.progress.ProgressManager;
35 import com.intellij.openapi.progress.Task;
36 import com.intellij.openapi.project.Project;
37 import com.intellij.openapi.project.ProjectManager;
38 import com.intellij.openapi.project.ProjectManagerListener;
39 import com.intellij.openapi.ui.DialogWrapper;
40 import com.intellij.openapi.util.Comparing;
41 import com.intellij.openapi.util.Disposer;
42 import com.intellij.openapi.util.IconLoader;
43 import com.intellij.openapi.util.Key;
44 import com.intellij.openapi.wm.ToolWindow;
45 import com.intellij.openapi.wm.ToolWindowAnchor;
46 import com.intellij.openapi.wm.ToolWindowManager;
47 import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter;
48 import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
49 import com.intellij.ui.AppUIUtil;
50 import com.intellij.ui.ColorUtil;
51 import com.intellij.ui.LayeredIcon;
52 import com.intellij.ui.content.*;
53 import com.intellij.ui.docking.DockManager;
54 import com.intellij.util.SmartList;
55 import com.intellij.util.concurrency.Semaphore;
56 import com.intellij.util.containers.ContainerUtil;
57 import com.intellij.util.ui.GraphicsUtil;
58 import com.intellij.util.ui.UIUtil;
59 import gnu.trove.THashMap;
60 import gnu.trove.THashSet;
61 import org.jetbrains.annotations.NotNull;
62 import org.jetbrains.annotations.Nullable;
66 import java.awt.geom.Ellipse2D;
68 import java.util.List;
70 public class RunContentManagerImpl implements RunContentManager, Disposable {
71 public static final Key<Boolean> ALWAYS_USE_DEFAULT_STOPPING_BEHAVIOUR_KEY = Key.create("ALWAYS_USE_DEFAULT_STOPPING_BEHAVIOUR_KEY");
72 private static final Logger LOG = Logger.getInstance(RunContentManagerImpl.class);
73 private static final Key<RunContentDescriptor> DESCRIPTOR_KEY = Key.create("Descriptor");
75 private final Project myProject;
76 private final Map<String, ContentManager> myToolwindowIdToContentManagerMap = new THashMap<String, ContentManager>();
77 private final Map<String, Icon> myToolwindowIdToBaseIconMap = new THashMap<String, Icon>();
79 private final Map<RunContentListener, Disposable> myListeners = new THashMap<RunContentListener, Disposable>();
80 private final LinkedList<String> myToolwindowIdZBuffer = new LinkedList<String>();
82 public RunContentManagerImpl(@NotNull Project project, @NotNull DockManager dockManager) {
84 DockableGridContainerFactory containerFactory = new DockableGridContainerFactory();
85 dockManager.register(DockableGridContainerFactory.TYPE, containerFactory);
86 Disposer.register(myProject, containerFactory);
88 AppUIUtil.invokeOnEdt(new Runnable() {
93 }, myProject.getDisposed());
96 // must be called on EDT
98 ToolWindowManagerEx toolWindowManager = ToolWindowManagerEx.getInstanceEx(myProject);
99 if (toolWindowManager == null) {
103 for (Executor executor : ExecutorRegistry.getInstance().getRegisteredExecutors()) {
104 registerToolwindow(executor, toolWindowManager);
107 toolWindowManager.addToolWindowManagerListener(new ToolWindowManagerAdapter() {
109 public void stateChanged() {
110 if (myProject.isDisposed()) {
114 ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
115 Set<String> currentWindows = new THashSet<String>();
116 ContainerUtil.addAll(currentWindows, toolWindowManager.getToolWindowIds());
117 myToolwindowIdZBuffer.retainAll(currentWindows);
119 final String activeToolWindowId = toolWindowManager.getActiveToolWindowId();
120 if (activeToolWindowId != null) {
121 if (myToolwindowIdZBuffer.remove(activeToolWindowId)) {
122 myToolwindowIdZBuffer.addFirst(activeToolWindowId);
130 public void dispose() {
133 private void registerToolwindow(@NotNull final Executor executor, @NotNull ToolWindowManagerEx toolWindowManager) {
134 final String toolWindowId = executor.getToolWindowId();
135 if (toolWindowManager.getToolWindow(toolWindowId) != null) {
139 final ToolWindow toolWindow = toolWindowManager.registerToolWindow(toolWindowId, true, ToolWindowAnchor.BOTTOM, this, true);
140 final ContentManager contentManager = toolWindow.getContentManager();
141 contentManager.addDataProvider(new DataProvider() {
142 private int myInsideGetData = 0;
145 public Object getData(String dataId) {
148 if (PlatformDataKeys.HELP_ID.is(dataId)) {
149 return executor.getHelpId();
152 return myInsideGetData == 1 ? DataManager.getInstance().getDataContext(contentManager.getComponent()).getData(dataId) : null;
161 toolWindow.setIcon(executor.getToolWindowIcon());
162 myToolwindowIdToBaseIconMap.put(toolWindowId, executor.getToolWindowIcon());
163 new ContentManagerWatcher(toolWindow, contentManager);
164 contentManager.addContentManagerListener(new ContentManagerAdapter() {
166 public void selectionChanged(final ContentManagerEvent event) {
167 Content content = event.getContent();
168 getSyncPublisher().contentSelected(content == null ? null : getRunContentDescriptorByContent(content), executor);
171 myToolwindowIdToContentManagerMap.put(toolWindowId, contentManager);
172 Disposer.register(contentManager, new Disposable() {
174 public void dispose() {
175 myToolwindowIdToContentManagerMap.remove(toolWindowId).removeAllContents(true);
176 myToolwindowIdZBuffer.remove(toolWindowId);
177 myToolwindowIdToBaseIconMap.remove(toolWindowId);
180 myToolwindowIdZBuffer.addLast(toolWindowId);
183 private RunContentWithExecutorListener getSyncPublisher() {
184 return myProject.getMessageBus().syncPublisher(TOPIC);
188 public void toFrontRunContent(final Executor requestor, final ProcessHandler handler) {
189 final RunContentDescriptor descriptor = getDescriptorBy(handler, requestor);
190 if (descriptor == null) {
193 toFrontRunContent(requestor, descriptor);
197 public void toFrontRunContent(final Executor requestor, final RunContentDescriptor descriptor) {
198 ApplicationManager.getApplication().invokeLater(new Runnable() {
201 ContentManager contentManager = getContentManagerForRunner(requestor);
202 Content content = getRunContentByDescriptor(contentManager, descriptor);
203 if (content != null) {
204 contentManager.setSelectedContent(content);
205 ToolWindowManager.getInstance(myProject).getToolWindow(requestor.getToolWindowId()).show(null);
208 }, myProject.getDisposed());
212 public void hideRunContent(@NotNull final Executor executor, final RunContentDescriptor descriptor) {
213 ApplicationManager.getApplication().invokeLater(new Runnable() {
216 ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(executor.getToolWindowId());
217 if (toolWindow != null) {
218 toolWindow.hide(null);
221 }, myProject.getDisposed());
226 public RunContentDescriptor getSelectedContent(final Executor executor) {
227 final Content selectedContent = getContentManagerForRunner(executor).getSelectedContent();
228 return selectedContent != null ? getRunContentDescriptorByContent(selectedContent) : null;
233 public RunContentDescriptor getSelectedContent() {
234 for (String activeWindow : myToolwindowIdZBuffer) {
235 final ContentManager contentManager = myToolwindowIdToContentManagerMap.get(activeWindow);
236 if (contentManager == null) {
240 final Content selectedContent = contentManager.getSelectedContent();
241 if (selectedContent == null) {
242 if (contentManager.getContentCount() == 0) {
243 // continue to the next window if the content manager is empty
247 // stop iteration over windows because there is some content in the window and the window is the last used one
251 // here we have selected content
252 return getRunContentDescriptorByContent(selectedContent);
259 public boolean removeRunContent(@NotNull final Executor executor, final RunContentDescriptor descriptor) {
260 final ContentManager contentManager = getContentManagerForRunner(executor);
261 final Content content = getRunContentByDescriptor(contentManager, descriptor);
262 return content != null && contentManager.removeContent(content, true);
266 public void showRunContent(@NotNull Executor executor, @NotNull RunContentDescriptor descriptor) {
267 showRunContent(executor, descriptor, descriptor.getExecutionId());
270 public void showRunContent(@NotNull final Executor executor, @NotNull final RunContentDescriptor descriptor, final long executionId) {
271 if (ApplicationManager.getApplication().isUnitTestMode()) {
275 final ContentManager contentManager = getContentManagerForRunner(executor);
276 RunContentDescriptor oldDescriptor = chooseReuseContentForDescriptor(contentManager, descriptor, executionId, descriptor.getDisplayName());
277 final Content content;
278 if (oldDescriptor == null) {
279 content = createNewContent(contentManager, descriptor, executor);
280 Icon icon = descriptor.getIcon();
281 content.setIcon(icon == null ? executor.getToolWindowIcon() : icon);
284 content = oldDescriptor.getAttachedContent();
285 LOG.assertTrue(content != null);
286 getSyncPublisher().contentRemoved(oldDescriptor, executor);
287 Disposer.dispose(oldDescriptor); // is of the same category, can be reused
290 content.setExecutionId(executionId);
291 content.setComponent(descriptor.getComponent());
292 content.setPreferredFocusedComponent(descriptor.getPreferredFocusComputable());
293 content.putUserData(DESCRIPTOR_KEY, descriptor);
294 final ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(executor.getToolWindowId());
295 final ProcessHandler processHandler = descriptor.getProcessHandler();
296 if (processHandler != null) {
297 final ProcessAdapter processAdapter = new ProcessAdapter() {
299 public void startNotified(final ProcessEvent event) {
300 UIUtil.invokeLaterIfNeeded(new Runnable() {
303 toolWindow.setIcon(getLiveIndicator(myToolwindowIdToBaseIconMap.get(executor.getToolWindowId())));
309 public void processTerminated(final ProcessEvent event) {
310 ApplicationManager.getApplication().invokeLater(new Runnable() {
313 final Icon icon = descriptor.getIcon();
315 boolean alive = false;
316 String toolWindowId = executor.getToolWindowId();
317 ContentManager manager = myToolwindowIdToContentManagerMap.get(toolWindowId);
318 for (Content content : manager.getContents()) {
319 RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
320 if (descriptor != null) {
321 ProcessHandler handler = descriptor.getProcessHandler();
322 if (handler != null && !handler.isProcessTerminated()) {
329 final boolean finalAlive = alive;
330 UIUtil.invokeLaterIfNeeded(new Runnable() {
333 toolWindow.setIcon(finalAlive
334 ? getLiveIndicator(myToolwindowIdToBaseIconMap.get(executor.getToolWindowId()))
335 : myToolwindowIdToBaseIconMap.get(executor.getToolWindowId()));
336 content.setIcon(icon == null ? executor.getDisabledIcon() : IconLoader.getTransparentIcon(icon));
343 processHandler.addProcessListener(processAdapter);
344 final Disposable disposer = content.getDisposer();
345 if (disposer != null) {
346 Disposer.register(disposer, new Disposable() {
348 public void dispose() {
349 processHandler.removeProcessListener(processAdapter);
354 content.setDisplayName(descriptor.getDisplayName());
355 descriptor.setAttachedContent(content);
356 content.getManager().setSelectedContent(content);
358 if (!descriptor.isActivateToolWindowWhenAdded()) {
362 ApplicationManager.getApplication().invokeLater(new Runnable() {
365 ToolWindow window = ToolWindowManager.getInstance(myProject).getToolWindow(executor.getToolWindowId());
366 // let's activate tool window, but don't move focus
368 // window.show() isn't valid here, because it will not
369 // mark the window as "last activated" windows and thus
370 // some action like navigation up/down in stacktrace wont
372 descriptor.getPreferredFocusComputable();
373 window.activate(descriptor.getActivationCallback(), descriptor.isAutoFocusContent(), descriptor.isAutoFocusContent());
375 }, myProject.getDisposed());
378 private final static int INDICATOR_SIZE = 4;
379 private static Icon getLiveIndicator(final Icon base) {
380 return new LayeredIcon(base, new Icon() {
382 public void paintIcon(Component c, Graphics g, int x, int y) {
383 Graphics2D g2d = (Graphics2D)g.create();
385 GraphicsUtil.setupAAPainting(g2d);
386 g2d.setColor(Color.GREEN);
387 Ellipse2D.Double shape =
388 new Ellipse2D.Double(x + getIconWidth() - INDICATOR_SIZE, y + getIconHeight() - INDICATOR_SIZE, INDICATOR_SIZE, INDICATOR_SIZE);
390 g2d.setColor(ColorUtil.withAlpha(Color.BLACK, .40));
399 public int getIconWidth() {
400 return base != null ? base.getIconWidth() : 13;
404 public int getIconHeight() {
405 return base != null ? base.getIconHeight() : 13;
413 public RunContentDescriptor getReuseContent(final Executor requestor, DataContext dataContext) {
414 if (ApplicationManager.getApplication().isUnitTestMode()) {
417 //noinspection deprecation
418 return getReuseContent(requestor, GenericProgramRunner.CONTENT_TO_REUSE_DATA_KEY.getData(dataContext));
424 public RunContentDescriptor getReuseContent(Executor requestor, @Nullable RunContentDescriptor contentToReuse) {
425 if (ApplicationManager.getApplication().isUnitTestMode()) {
428 if (contentToReuse != null) {
429 return contentToReuse;
431 return chooseReuseContentForDescriptor(getContentManagerForRunner(requestor), null, 0L, null);
436 public RunContentDescriptor getReuseContent(Executor requestor, @NotNull ExecutionEnvironment executionEnvironment) {
437 return getReuseContent(executionEnvironment);
442 public RunContentDescriptor getReuseContent(@NotNull ExecutionEnvironment executionEnvironment) {
443 if (ApplicationManager.getApplication().isUnitTestMode()) return null;
444 RunContentDescriptor contentToReuse = executionEnvironment.getContentToReuse();
445 if (contentToReuse != null) {
446 return contentToReuse;
449 final ContentManager contentManager = getContentManagerForRunner(executionEnvironment.getExecutor());
450 return chooseReuseContentForDescriptor(contentManager, null, executionEnvironment.getExecutionId(),
451 executionEnvironment.toString());
455 public RunContentDescriptor findContentDescriptor(final Executor requestor, final ProcessHandler handler) {
456 return getDescriptorBy(handler, requestor);
460 public void showRunContent(@NotNull Executor info, @NotNull RunContentDescriptor descriptor, @Nullable RunContentDescriptor contentToReuse) {
461 copyContentAndBehavior(descriptor, contentToReuse);
462 showRunContent(info, descriptor, descriptor.getExecutionId());
465 public static void copyContentAndBehavior(@NotNull RunContentDescriptor descriptor, @Nullable RunContentDescriptor contentToReuse) {
466 if (contentToReuse != null) {
467 Content attachedContent = contentToReuse.getAttachedContent();
468 if (attachedContent != null && attachedContent.isValid()) {
469 descriptor.setAttachedContent(attachedContent);
471 if (contentToReuse.isReuseToolWindowActivation()) {
472 descriptor.setActivateToolWindowWhenAdded(contentToReuse.isActivateToolWindowWhenAdded());
478 private static RunContentDescriptor chooseReuseContentForDescriptor(@NotNull ContentManager contentManager,
479 @Nullable RunContentDescriptor descriptor,
481 @Nullable String preferredName) {
482 Content content = null;
483 if (descriptor != null) {
484 //Stage one: some specific descriptors (like AnalyzeStacktrace) cannot be reused at all
485 if (descriptor.isContentReuseProhibited()) {
488 //Stage two: try to get content from descriptor itself
489 final Content attachedContent = descriptor.getAttachedContent();
491 if (attachedContent != null
492 && attachedContent.isValid()
493 && contentManager.getIndexOfContent(attachedContent) != -1
494 && (Comparing.equal(descriptor.getDisplayName(), attachedContent.getDisplayName()) || !attachedContent.isPinned())) {
495 content = attachedContent;
498 //Stage three: choose the content with name we prefer
499 if (content == null) {
500 content = getContentFromManager(contentManager, preferredName, executionId);
502 if (content == null || !isTerminated(content) || (content.getExecutionId() == executionId && executionId != 0)) {
505 final RunContentDescriptor oldDescriptor = getRunContentDescriptorByContent(content);
506 if (oldDescriptor != null && !oldDescriptor.isContentReuseProhibited() ) {
507 //content.setExecutionId(executionId);
508 return oldDescriptor;
515 private static Content getContentFromManager(ContentManager contentManager, @Nullable String preferredName, long executionId) {
516 ArrayList<Content> contents = new ArrayList<Content>(Arrays.asList(contentManager.getContents()));
517 Content first = contentManager.getSelectedContent();
518 if (first != null && contents.remove(first)) {//selected content should be checked first
519 contents.add(0, first);
521 if (preferredName != null) {//try to match content with specified preferred name
522 for (Content c : contents) {
523 if (canReuseContent(c, executionId) && preferredName.equals(c.getDisplayName())) {
528 for (Content c : contents) {//return first "good" content
529 if (canReuseContent(c, executionId)) {
536 private static boolean canReuseContent(Content c, long executionId) {
537 return c != null && !c.isPinned() && isTerminated(c) && !(c.getExecutionId() == executionId && executionId != 0);
541 private ContentManager getContentManagerForRunner(final Executor executor) {
542 final ContentManager contentManager = myToolwindowIdToContentManagerMap.get(executor.getToolWindowId());
543 if (contentManager == null) {
544 LOG.error("Runner " + executor.getId() + " is not registered");
546 //noinspection ConstantConditions
547 return contentManager;
550 private Content createNewContent(final ContentManager contentManager, final RunContentDescriptor descriptor, Executor executor) {
551 final String processDisplayName = descriptor.getDisplayName();
552 final Content content = ContentFactory.SERVICE.getInstance().createContent(descriptor.getComponent(), processDisplayName, true);
553 content.putUserData(DESCRIPTOR_KEY, descriptor);
554 content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
555 contentManager.addContent(content);
556 new CloseListener(content, executor);
560 private static boolean isTerminated(@NotNull Content content) {
561 RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
562 ProcessHandler processHandler = descriptor == null ? null : descriptor.getProcessHandler();
563 return processHandler == null || processHandler.isProcessTerminated();
567 private static RunContentDescriptor getRunContentDescriptorByContent(@NotNull Content content) {
568 return content.getUserData(DESCRIPTOR_KEY);
573 public ToolWindow getToolWindowByDescriptor(@NotNull RunContentDescriptor descriptor) {
574 for (Map.Entry<String, ContentManager> entry : myToolwindowIdToContentManagerMap.entrySet()) {
575 if (getRunContentByDescriptor(entry.getValue(), descriptor) != null) {
576 return ToolWindowManager.getInstance(myProject).getToolWindow(entry.getKey());
583 private static Content getRunContentByDescriptor(@NotNull ContentManager contentManager, @NotNull RunContentDescriptor descriptor) {
584 for (Content content : contentManager.getContents()) {
585 if (descriptor.equals(content.getUserData(DESCRIPTOR_KEY))) {
593 public void addRunContentListener(@NotNull final RunContentListener listener, final Executor executor) {
594 final Disposable disposable = Disposer.newDisposable();
595 myProject.getMessageBus().connect(disposable).subscribe(TOPIC, new RunContentWithExecutorListener() {
597 public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor2) {
598 if (executor2.equals(executor)) {
599 listener.contentSelected(descriptor);
604 public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor2) {
605 if (executor2.equals(executor)) {
606 listener.contentRemoved(descriptor);
610 myListeners.put(listener, disposable);
614 public void addRunContentListener(@NotNull final RunContentListener listener) {
615 final Disposable disposable = Disposer.newDisposable();
616 myProject.getMessageBus().connect(disposable).subscribe(TOPIC, new RunContentWithExecutorListener() {
618 public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) {
619 listener.contentSelected(descriptor);
623 public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) {
624 listener.contentRemoved(descriptor);
627 myListeners.put(listener, disposable);
632 public List<RunContentDescriptor> getAllDescriptors() {
633 if (myToolwindowIdToContentManagerMap.isEmpty()) {
634 return Collections.emptyList();
637 List<RunContentDescriptor> descriptors = new SmartList<RunContentDescriptor>();
638 for (String id : myToolwindowIdToContentManagerMap.keySet()) {
639 for (Content content : myToolwindowIdToContentManagerMap.get(id).getContents()) {
640 RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
641 if (descriptor != null) {
642 descriptors.add(descriptor);
650 public void removeRunContentListener(final RunContentListener listener) {
651 Disposable disposable = myListeners.remove(listener);
652 if (disposable != null) {
653 Disposer.dispose(disposable);
658 private RunContentDescriptor getDescriptorBy(ProcessHandler handler, Executor runnerInfo) {
659 for (Content content : getContentManagerForRunner(runnerInfo).getContents()) {
660 RunContentDescriptor runContentDescriptor = getRunContentDescriptorByContent(content);
661 assert runContentDescriptor != null;
662 if (runContentDescriptor.getProcessHandler() == handler) {
663 return runContentDescriptor;
669 private class CloseListener extends ContentManagerAdapter implements ProjectManagerListener {
670 private Content myContent;
671 private final Executor myExecutor;
673 private CloseListener(@NotNull final Content content, @NotNull Executor executor) {
675 content.getManager().addContentManagerListener(this);
676 ProjectManager.getInstance().addProjectManagerListener(this);
677 myExecutor = executor;
681 public void contentRemoved(final ContentManagerEvent event) {
682 final Content content = event.getContent();
683 if (content == myContent) {
688 private void dispose() {
689 if (myContent == null) return;
691 final Content content = myContent;
693 RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
694 getSyncPublisher().contentRemoved(descriptor, myExecutor);
695 if (descriptor != null) {
696 Disposer.dispose(descriptor);
700 content.getManager().removeContentManagerListener(this);
701 ProjectManager.getInstance().removeProjectManagerListener(this);
702 content.release(); // don't invoke myContent.release() because myContent becomes null after destroyProcess()
708 public void contentRemoveQuery(final ContentManagerEvent event) {
709 if (event.getContent() == myContent) {
710 final boolean canClose = closeQuery(false);
718 public void projectOpened(final Project project) {
722 public void projectClosed(final Project project) {
723 if (myContent != null && project == myProject) {
724 myContent.getManager().removeContent(myContent, true);
725 dispose(); // Dispose content even if content manager refused to.
730 public boolean canCloseProject(final Project project) {
731 if (project != myProject) return true;
733 if (myContent == null) return true;
735 final boolean canClose = closeQuery(true);
737 myContent.getManager().removeContent(myContent, true);
744 public void projectClosing(final Project project) {
747 private boolean closeQuery(boolean modal) {
748 final RunContentDescriptor descriptor = getRunContentDescriptorByContent(myContent);
749 if (descriptor == null) {
753 final ProcessHandler processHandler = descriptor.getProcessHandler();
754 if (processHandler == null || processHandler.isProcessTerminated() || processHandler.isProcessTerminating()) {
757 final boolean destroyProcess;
758 //noinspection deprecation
759 if (processHandler.isSilentlyDestroyOnClose() || Boolean.TRUE.equals(processHandler.getUserData(ProcessHandler.SILENTLY_DESTROY_ON_CLOSE))) {
760 destroyProcess = true;
763 //todo[nik] this is a temporary solution for the following problem: some configurations should not allow user to choose between 'terminating' and 'detaching'
764 final boolean useDefault = Boolean.TRUE.equals(processHandler.getUserData(ALWAYS_USE_DEFAULT_STOPPING_BEHAVIOUR_KEY));
765 final TerminateRemoteProcessDialog.TerminateOption option = new TerminateRemoteProcessDialog.TerminateOption(processHandler.detachIsDefault(), useDefault);
766 final int rc = TerminateRemoteProcessDialog.show(myProject, descriptor.getDisplayName(), option);
767 if (rc != DialogWrapper.OK_EXIT_CODE) return false;
768 destroyProcess = !option.isToBeShown();
770 if (destroyProcess) {
771 processHandler.destroyProcess();
774 processHandler.detachProcess();
776 waitForProcess(descriptor, modal);
781 private void waitForProcess(final RunContentDescriptor descriptor, final boolean modal) {
782 final ProcessHandler processHandler = descriptor.getProcessHandler();
783 final boolean killable = !modal && (processHandler instanceof KillableProcess) && ((KillableProcess)processHandler).canKillProcess();
785 String title = ExecutionBundle.message("terminating.process.progress.title", descriptor.getDisplayName());
786 ProgressManager.getInstance().run(new Task.Backgroundable(myProject, title, true) {
790 String cancelText= ExecutionBundle.message("terminating.process.progress.kill");
791 setCancelText(cancelText);
792 setCancelTooltipText(cancelText);
797 public boolean isConditionalModal() {
802 public boolean shouldStartInBackground() {
807 public void run(@NotNull final ProgressIndicator progressIndicator) {
808 final Semaphore semaphore = new Semaphore();
811 ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
814 final ProcessHandler processHandler = descriptor.getProcessHandler();
816 if (processHandler != null) {
817 processHandler.waitFor();
826 progressIndicator.setText(ExecutionBundle.message("waiting.for.vm.detach.progress.text"));
827 ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
831 if (progressIndicator.isCanceled() || !progressIndicator.isRunning()) {
836 //noinspection SynchronizeOnThis
837 synchronized (this) {
838 //noinspection SynchronizeOnThis
842 catch (InterruptedException ignore) {
852 public void onCancel() {
853 if (killable && !processHandler.isProcessTerminated()) {
854 ((KillableProcess)processHandler).killProcess();