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 boolean alive = false;
314 String toolWindowId = executor.getToolWindowId();
315 ContentManager manager = myToolwindowIdToContentManagerMap.get(toolWindowId);
316 for (Content content : manager.getContents()) {
317 RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
318 if (descriptor != null) {
319 ProcessHandler handler = descriptor.getProcessHandler();
320 if (handler != null && !handler.isProcessTerminated()) {
326 Icon base = myToolwindowIdToBaseIconMap.get(toolWindowId);
327 toolWindow.setIcon(alive ? getLiveIndicator(base) : base);
329 Icon icon = descriptor.getIcon();
330 content.setIcon(icon == null ? executor.getDisabledIcon() : IconLoader.getTransparentIcon(icon));
335 processHandler.addProcessListener(processAdapter);
336 final Disposable disposer = content.getDisposer();
337 if (disposer != null) {
338 Disposer.register(disposer, new Disposable() {
340 public void dispose() {
341 processHandler.removeProcessListener(processAdapter);
346 content.setDisplayName(descriptor.getDisplayName());
347 descriptor.setAttachedContent(content);
348 content.getManager().setSelectedContent(content);
350 if (!descriptor.isActivateToolWindowWhenAdded()) {
354 ApplicationManager.getApplication().invokeLater(new Runnable() {
357 ToolWindow window = ToolWindowManager.getInstance(myProject).getToolWindow(executor.getToolWindowId());
358 // let's activate tool window, but don't move focus
360 // window.show() isn't valid here, because it will not
361 // mark the window as "last activated" windows and thus
362 // some action like navigation up/down in stacktrace wont
364 descriptor.getPreferredFocusComputable();
365 window.activate(descriptor.getActivationCallback(), descriptor.isAutoFocusContent(), descriptor.isAutoFocusContent());
367 }, myProject.getDisposed());
370 private final static int INDICATOR_SIZE = 4;
371 private static Icon getLiveIndicator(final Icon base) {
372 return new LayeredIcon(base, new Icon() {
374 public void paintIcon(Component c, Graphics g, int x, int y) {
375 Graphics2D g2d = (Graphics2D)g.create();
377 GraphicsUtil.setupAAPainting(g2d);
378 g2d.setColor(Color.GREEN);
379 Ellipse2D.Double shape =
380 new Ellipse2D.Double(x + getIconWidth() - INDICATOR_SIZE, y + getIconHeight() - INDICATOR_SIZE, INDICATOR_SIZE, INDICATOR_SIZE);
382 g2d.setColor(ColorUtil.withAlpha(Color.BLACK, .40));
391 public int getIconWidth() {
392 return base != null ? base.getIconWidth() : 13;
396 public int getIconHeight() {
397 return base != null ? base.getIconHeight() : 13;
405 public RunContentDescriptor getReuseContent(final Executor requestor, DataContext dataContext) {
406 if (ApplicationManager.getApplication().isUnitTestMode()) {
409 //noinspection deprecation
410 return getReuseContent(requestor, GenericProgramRunner.CONTENT_TO_REUSE_DATA_KEY.getData(dataContext));
416 public RunContentDescriptor getReuseContent(Executor requestor, @Nullable RunContentDescriptor contentToReuse) {
417 if (ApplicationManager.getApplication().isUnitTestMode()) {
420 if (contentToReuse != null) {
421 return contentToReuse;
423 return chooseReuseContentForDescriptor(getContentManagerForRunner(requestor), null, 0L, null);
428 public RunContentDescriptor getReuseContent(Executor requestor, @NotNull ExecutionEnvironment executionEnvironment) {
429 return getReuseContent(executionEnvironment);
434 public RunContentDescriptor getReuseContent(@NotNull ExecutionEnvironment executionEnvironment) {
435 if (ApplicationManager.getApplication().isUnitTestMode()) return null;
436 RunContentDescriptor contentToReuse = executionEnvironment.getContentToReuse();
437 if (contentToReuse != null) {
438 return contentToReuse;
441 final ContentManager contentManager = getContentManagerForRunner(executionEnvironment.getExecutor());
442 return chooseReuseContentForDescriptor(contentManager, null, executionEnvironment.getExecutionId(),
443 executionEnvironment.toString());
447 public RunContentDescriptor findContentDescriptor(final Executor requestor, final ProcessHandler handler) {
448 return getDescriptorBy(handler, requestor);
452 public void showRunContent(@NotNull Executor info, @NotNull RunContentDescriptor descriptor, @Nullable RunContentDescriptor contentToReuse) {
453 copyContentAndBehavior(descriptor, contentToReuse);
454 showRunContent(info, descriptor, descriptor.getExecutionId());
457 public static void copyContentAndBehavior(@NotNull RunContentDescriptor descriptor, @Nullable RunContentDescriptor contentToReuse) {
458 if (contentToReuse != null) {
459 Content attachedContent = contentToReuse.getAttachedContent();
460 if (attachedContent != null && attachedContent.isValid()) {
461 descriptor.setAttachedContent(attachedContent);
463 if (contentToReuse.isReuseToolWindowActivation()) {
464 descriptor.setActivateToolWindowWhenAdded(contentToReuse.isActivateToolWindowWhenAdded());
470 private static RunContentDescriptor chooseReuseContentForDescriptor(@NotNull ContentManager contentManager,
471 @Nullable RunContentDescriptor descriptor,
473 @Nullable String preferredName) {
474 Content content = null;
475 if (descriptor != null) {
476 //Stage one: some specific descriptors (like AnalyzeStacktrace) cannot be reused at all
477 if (descriptor.isContentReuseProhibited()) {
480 //Stage two: try to get content from descriptor itself
481 final Content attachedContent = descriptor.getAttachedContent();
483 if (attachedContent != null
484 && attachedContent.isValid()
485 && contentManager.getIndexOfContent(attachedContent) != -1
486 && (Comparing.equal(descriptor.getDisplayName(), attachedContent.getDisplayName()) || !attachedContent.isPinned())) {
487 content = attachedContent;
490 //Stage three: choose the content with name we prefer
491 if (content == null) {
492 content = getContentFromManager(contentManager, preferredName, executionId);
494 if (content == null || !isTerminated(content) || (content.getExecutionId() == executionId && executionId != 0)) {
497 final RunContentDescriptor oldDescriptor = getRunContentDescriptorByContent(content);
498 if (oldDescriptor != null && !oldDescriptor.isContentReuseProhibited() ) {
499 //content.setExecutionId(executionId);
500 return oldDescriptor;
507 private static Content getContentFromManager(ContentManager contentManager, @Nullable String preferredName, long executionId) {
508 ArrayList<Content> contents = new ArrayList<Content>(Arrays.asList(contentManager.getContents()));
509 Content first = contentManager.getSelectedContent();
510 if (first != null && contents.remove(first)) {//selected content should be checked first
511 contents.add(0, first);
513 if (preferredName != null) {//try to match content with specified preferred name
514 for (Content c : contents) {
515 if (canReuseContent(c, executionId) && preferredName.equals(c.getDisplayName())) {
520 for (Content c : contents) {//return first "good" content
521 if (canReuseContent(c, executionId)) {
528 private static boolean canReuseContent(Content c, long executionId) {
529 return c != null && !c.isPinned() && isTerminated(c) && !(c.getExecutionId() == executionId && executionId != 0);
533 private ContentManager getContentManagerForRunner(final Executor executor) {
534 final ContentManager contentManager = myToolwindowIdToContentManagerMap.get(executor.getToolWindowId());
535 if (contentManager == null) {
536 LOG.error("Runner " + executor.getId() + " is not registered");
538 //noinspection ConstantConditions
539 return contentManager;
542 private Content createNewContent(final ContentManager contentManager, final RunContentDescriptor descriptor, Executor executor) {
543 final String processDisplayName = descriptor.getDisplayName();
544 final Content content = ContentFactory.SERVICE.getInstance().createContent(descriptor.getComponent(), processDisplayName, true);
545 content.putUserData(DESCRIPTOR_KEY, descriptor);
546 content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
547 contentManager.addContent(content);
548 new CloseListener(content, executor);
552 private static boolean isTerminated(@NotNull Content content) {
553 RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
554 ProcessHandler processHandler = descriptor == null ? null : descriptor.getProcessHandler();
555 return processHandler == null || processHandler.isProcessTerminated();
559 private static RunContentDescriptor getRunContentDescriptorByContent(@NotNull Content content) {
560 return content.getUserData(DESCRIPTOR_KEY);
565 public ToolWindow getToolWindowByDescriptor(@NotNull RunContentDescriptor descriptor) {
566 for (Map.Entry<String, ContentManager> entry : myToolwindowIdToContentManagerMap.entrySet()) {
567 if (getRunContentByDescriptor(entry.getValue(), descriptor) != null) {
568 return ToolWindowManager.getInstance(myProject).getToolWindow(entry.getKey());
575 private static Content getRunContentByDescriptor(@NotNull ContentManager contentManager, @NotNull RunContentDescriptor descriptor) {
576 for (Content content : contentManager.getContents()) {
577 if (descriptor.equals(content.getUserData(DESCRIPTOR_KEY))) {
585 public void addRunContentListener(@NotNull final RunContentListener listener, final Executor executor) {
586 final Disposable disposable = Disposer.newDisposable();
587 myProject.getMessageBus().connect(disposable).subscribe(TOPIC, new RunContentWithExecutorListener() {
589 public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor2) {
590 if (executor2.equals(executor)) {
591 listener.contentSelected(descriptor);
596 public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor2) {
597 if (executor2.equals(executor)) {
598 listener.contentRemoved(descriptor);
602 myListeners.put(listener, disposable);
606 public void addRunContentListener(@NotNull final RunContentListener listener) {
607 final Disposable disposable = Disposer.newDisposable();
608 myProject.getMessageBus().connect(disposable).subscribe(TOPIC, new RunContentWithExecutorListener() {
610 public void contentSelected(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) {
611 listener.contentSelected(descriptor);
615 public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) {
616 listener.contentRemoved(descriptor);
619 myListeners.put(listener, disposable);
624 public List<RunContentDescriptor> getAllDescriptors() {
625 if (myToolwindowIdToContentManagerMap.isEmpty()) {
626 return Collections.emptyList();
629 List<RunContentDescriptor> descriptors = new SmartList<RunContentDescriptor>();
630 for (String id : myToolwindowIdToContentManagerMap.keySet()) {
631 for (Content content : myToolwindowIdToContentManagerMap.get(id).getContents()) {
632 RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
633 if (descriptor != null) {
634 descriptors.add(descriptor);
642 public void removeRunContentListener(final RunContentListener listener) {
643 Disposable disposable = myListeners.remove(listener);
644 if (disposable != null) {
645 Disposer.dispose(disposable);
650 private RunContentDescriptor getDescriptorBy(ProcessHandler handler, Executor runnerInfo) {
651 for (Content content : getContentManagerForRunner(runnerInfo).getContents()) {
652 RunContentDescriptor runContentDescriptor = getRunContentDescriptorByContent(content);
653 assert runContentDescriptor != null;
654 if (runContentDescriptor.getProcessHandler() == handler) {
655 return runContentDescriptor;
661 private class CloseListener extends ContentManagerAdapter implements ProjectManagerListener {
662 private Content myContent;
663 private final Executor myExecutor;
665 private CloseListener(@NotNull final Content content, @NotNull Executor executor) {
667 content.getManager().addContentManagerListener(this);
668 ProjectManager.getInstance().addProjectManagerListener(this);
669 myExecutor = executor;
673 public void contentRemoved(final ContentManagerEvent event) {
674 final Content content = event.getContent();
675 if (content == myContent) {
680 private void dispose() {
681 if (myContent == null) return;
683 final Content content = myContent;
685 RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
686 getSyncPublisher().contentRemoved(descriptor, myExecutor);
687 if (descriptor != null) {
688 Disposer.dispose(descriptor);
692 content.getManager().removeContentManagerListener(this);
693 ProjectManager.getInstance().removeProjectManagerListener(this);
694 content.release(); // don't invoke myContent.release() because myContent becomes null after destroyProcess()
700 public void contentRemoveQuery(final ContentManagerEvent event) {
701 if (event.getContent() == myContent) {
702 final boolean canClose = closeQuery(false);
710 public void projectOpened(final Project project) {
714 public void projectClosed(final Project project) {
715 if (myContent != null && project == myProject) {
716 myContent.getManager().removeContent(myContent, true);
717 dispose(); // Dispose content even if content manager refused to.
722 public boolean canCloseProject(final Project project) {
723 if (project != myProject) return true;
725 if (myContent == null) return true;
727 final boolean canClose = closeQuery(true);
729 myContent.getManager().removeContent(myContent, true);
736 public void projectClosing(final Project project) {
739 private boolean closeQuery(boolean modal) {
740 final RunContentDescriptor descriptor = getRunContentDescriptorByContent(myContent);
741 if (descriptor == null) {
745 final ProcessHandler processHandler = descriptor.getProcessHandler();
746 if (processHandler == null || processHandler.isProcessTerminated() || processHandler.isProcessTerminating()) {
749 final boolean destroyProcess;
750 //noinspection deprecation
751 if (processHandler.isSilentlyDestroyOnClose() || Boolean.TRUE.equals(processHandler.getUserData(ProcessHandler.SILENTLY_DESTROY_ON_CLOSE))) {
752 destroyProcess = true;
755 //todo[nik] this is a temporary solution for the following problem: some configurations should not allow user to choose between 'terminating' and 'detaching'
756 final boolean useDefault = Boolean.TRUE.equals(processHandler.getUserData(ALWAYS_USE_DEFAULT_STOPPING_BEHAVIOUR_KEY));
757 final TerminateRemoteProcessDialog.TerminateOption option = new TerminateRemoteProcessDialog.TerminateOption(processHandler.detachIsDefault(), useDefault);
758 final int rc = TerminateRemoteProcessDialog.show(myProject, descriptor.getDisplayName(), option);
759 if (rc != DialogWrapper.OK_EXIT_CODE) return false;
760 destroyProcess = !option.isToBeShown();
762 if (destroyProcess) {
763 processHandler.destroyProcess();
766 processHandler.detachProcess();
768 waitForProcess(descriptor, modal);
773 private void waitForProcess(final RunContentDescriptor descriptor, final boolean modal) {
774 final ProcessHandler processHandler = descriptor.getProcessHandler();
775 final boolean killable = !modal && (processHandler instanceof KillableProcess) && ((KillableProcess)processHandler).canKillProcess();
777 String title = ExecutionBundle.message("terminating.process.progress.title", descriptor.getDisplayName());
778 ProgressManager.getInstance().run(new Task.Backgroundable(myProject, title, true) {
782 String cancelText= ExecutionBundle.message("terminating.process.progress.kill");
783 setCancelText(cancelText);
784 setCancelTooltipText(cancelText);
789 public boolean isConditionalModal() {
794 public boolean shouldStartInBackground() {
799 public void run(@NotNull final ProgressIndicator progressIndicator) {
800 final Semaphore semaphore = new Semaphore();
803 ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
806 final ProcessHandler processHandler = descriptor.getProcessHandler();
808 if (processHandler != null) {
809 processHandler.waitFor();
818 progressIndicator.setText(ExecutionBundle.message("waiting.for.vm.detach.progress.text"));
819 ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
823 if (progressIndicator.isCanceled() || !progressIndicator.isRunning()) {
828 //noinspection SynchronizeOnThis
829 synchronized (this) {
830 //noinspection SynchronizeOnThis
834 catch (InterruptedException ignore) {
844 public void onCancel() {
845 if (killable && !processHandler.isProcessTerminated()) {
846 ((KillableProcess)processHandler).killProcess();