IDEADEV-38479 Show run configuration's icon in "Run"-window
[fedora-idea.git] / platform / lang-impl / src / com / intellij / execution / ui / RunContentManagerImpl.java
blob0d98f59dd3d34a64d1bea66bb07ff0476e06e103
1 /*
2 * Copyright 2000-2009 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.GenericProgramRunner;
23 import com.intellij.ide.DataManager;
24 import com.intellij.ide.impl.ContentManagerWatcher;
25 import com.intellij.openapi.Disposable;
26 import com.intellij.openapi.actionSystem.DataConstants;
27 import com.intellij.openapi.actionSystem.DataContext;
28 import com.intellij.openapi.actionSystem.DataProvider;
29 import com.intellij.openapi.application.ApplicationManager;
30 import com.intellij.openapi.application.impl.LaterInvocator;
31 import com.intellij.openapi.diagnostic.Logger;
32 import com.intellij.openapi.progress.ProgressIndicator;
33 import com.intellij.openapi.progress.ProgressManager;
34 import com.intellij.openapi.project.Project;
35 import com.intellij.openapi.project.ProjectManager;
36 import com.intellij.openapi.project.ProjectManagerListener;
37 import com.intellij.openapi.ui.DialogWrapper;
38 import com.intellij.openapi.util.Disposer;
39 import com.intellij.openapi.util.IconLoader;
40 import com.intellij.openapi.util.Key;
41 import com.intellij.openapi.wm.ToolWindow;
42 import com.intellij.openapi.wm.ToolWindowAnchor;
43 import com.intellij.openapi.wm.ToolWindowManager;
44 import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter;
45 import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
46 import com.intellij.ui.content.*;
47 import com.intellij.util.EventDispatcher;
48 import com.intellij.util.IconUtil;
49 import com.intellij.util.concurrency.Semaphore;
50 import com.intellij.util.containers.HashMap;
51 import org.jetbrains.annotations.NotNull;
52 import org.jetbrains.annotations.Nullable;
54 import javax.swing.*;
55 import java.util.*;
57 public class RunContentManagerImpl implements RunContentManager, Disposable {
58 private static final Logger LOG = Logger.getInstance("#com.intellij.execution.ui.RunContentManagerImpl");
59 private static final Key<RunContentDescriptor> DESCRIPTOR_KEY = new Key<RunContentDescriptor>("Descriptor");
61 private final Project myProject;
62 private final Map<String, ContentManager> myToolwindowIdToContentManagerMap = new HashMap<String, ContentManager>();
64 private final Map<RunContentListener, MyRunContentListener> myListeners = new HashMap<RunContentListener, MyRunContentListener>();
65 private final EventDispatcher<MyRunContentListener> myEventDispatcher;
66 private final LinkedList<String> myToolwindowIdZbuffer = new LinkedList<String>();
68 public RunContentManagerImpl(Project project) {
69 myProject = project;
70 myEventDispatcher = EventDispatcher.create(MyRunContentListener.class);
73 public void init() {
74 final Executor[] executors = ExecutorRegistry.getInstance().getRegisteredExecutors();
75 for (Executor executor : executors) {
76 registerToolwindow(executor);
79 if (ToolWindowManager.getInstance(myProject) == null) return;
81 // To ensure ToolwindowManager had already initialized in its projectOpened.
82 SwingUtilities.invokeLater(new Runnable() {
83 public void run() {
84 if (myProject.isDisposed()) return;
85 ((ToolWindowManagerEx)ToolWindowManager.getInstance(myProject)).addToolWindowManagerListener(new ToolWindowManagerAdapter() {
86 public void stateChanged() {
87 if (myProject.isDisposed()) return;
89 ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
91 Set<String> currentWindows = new HashSet<String>();
92 String[] toolWindowIds = toolWindowManager.getToolWindowIds();
94 currentWindows.addAll(Arrays.asList(toolWindowIds));
95 myToolwindowIdZbuffer.retainAll(currentWindows);
97 final String activeToolWindowId = toolWindowManager.getActiveToolWindowId();
98 if (activeToolWindowId != null) {
99 if (myToolwindowIdZbuffer.remove(activeToolWindowId)) {
100 myToolwindowIdZbuffer.addFirst(activeToolWindowId);
109 public void dispose() {
112 private void unregisterToolwindow(final String id) {
113 final ContentManager manager = myToolwindowIdToContentManagerMap.get(id);
114 manager.removeAllContents(true);
115 myToolwindowIdToContentManagerMap.remove(id);
116 myToolwindowIdZbuffer.remove(id);
119 private void registerToolwindow(final Executor executor) {
120 final String toolWindowId = executor.getToolWindowId();
121 final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
122 if (toolWindowManager == null) return; //headless environment
123 if (toolWindowManager.getToolWindow(toolWindowId) != null) {
124 return;
127 final ToolWindow toolWindow = toolWindowManager.registerToolWindow(toolWindowId, true, ToolWindowAnchor.BOTTOM, this, true);
129 final ContentManager contentManager = toolWindow.getContentManager();
130 class MyDataProvider implements DataProvider {
131 private int myInsideGetData = 0;
132 public Object getData(String dataId) {
133 myInsideGetData ++;
134 try {
135 if(DataConstants.HELP_ID.equals(dataId)) {
136 return executor.getHelpId();
138 else {
139 return myInsideGetData == 1 ? DataManager.getInstance().getDataContext(contentManager.getComponent()).getData(dataId) : null;
141 } finally {
142 myInsideGetData--;
146 contentManager.addDataProvider(new MyDataProvider());
148 toolWindow.setIcon(executor.getToolWindowIcon());
149 new ContentManagerWatcher(toolWindow, contentManager);
150 contentManager.addContentManagerListener(new ContentManagerAdapter() {
151 public void selectionChanged(final ContentManagerEvent event) {
152 final Content content = event.getContent();
153 final RunContentDescriptor descriptor = content != null ? getRunContentDescriptorByContent(content) : null;
154 myEventDispatcher.getMulticaster().contentSelected(descriptor, toolWindowId);
157 myToolwindowIdToContentManagerMap.put(toolWindowId, contentManager);
158 Disposer.register(contentManager, new Disposable() {
159 public void dispose() {
160 unregisterToolwindow(toolWindowId);
163 myToolwindowIdZbuffer.addLast(toolWindowId);
166 public void toFrontRunContent(final Executor requestor, final ProcessHandler handler) {
167 final RunContentDescriptor descriptor = getDescriptorBy(handler, requestor);
168 if (descriptor == null) {
169 return;
171 toFrontRunContent(requestor, descriptor);
175 public void toFrontRunContent(final Executor requestor, final RunContentDescriptor descriptor) {
176 ApplicationManager.getApplication().invokeLater(new Runnable() {
177 public void run() {
178 final ContentManager contentManager = getContentManagerForRunner(requestor);
180 final Content content = getRunContentByDescriptor(contentManager, descriptor);
182 if (contentManager != null && content != null) {
183 contentManager.setSelectedContent(content);
185 if (content != null) {
186 final ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(requestor.getToolWindowId());
187 toolWindow.show(null);
194 public void hideRunContent(@NotNull final Executor executor, final RunContentDescriptor descriptor) {
195 ApplicationManager.getApplication().invokeLater(new Runnable() {
196 public void run() {
197 if (!myProject.isDisposed()) {
198 final String toolWindowId = executor.getToolWindowId();
199 final ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(toolWindowId);
200 if (toolWindow != null) {
201 toolWindow.hide(null);
208 @Nullable
209 public RunContentDescriptor getSelectedContent(final Executor executor) {
210 final ContentManager contentManager = getContentManagerForRunner(executor);
211 if (contentManager != null) {
212 final Content selectedContent = contentManager.getSelectedContent();
213 if (selectedContent != null) {
214 final RunContentDescriptor runContentDescriptorByContent = getRunContentDescriptorByContent(selectedContent);
215 if (runContentDescriptorByContent != null) {
216 return runContentDescriptorByContent;
220 return null;
223 @Nullable
224 public RunContentDescriptor getSelectedContent() {
225 final String activeWindow = myToolwindowIdZbuffer.isEmpty() ? null : myToolwindowIdZbuffer.getFirst();
227 if (activeWindow != null) {
228 final ContentManager contentManager = myToolwindowIdToContentManagerMap.get(activeWindow);
229 if (contentManager != null) {
230 final Content selectedContent = contentManager.getSelectedContent();
231 if (selectedContent != null) {
232 final RunContentDescriptor runContentDescriptorByContent = getRunContentDescriptorByContent(selectedContent);
233 if (runContentDescriptorByContent != null) {
234 return runContentDescriptorByContent;
239 return null;
242 public boolean removeRunContent(@NotNull final Executor executor, final RunContentDescriptor descriptor) {
243 final ContentManager contentManager = getContentManagerForRunner(executor);
244 final Content content = getRunContentByDescriptor(contentManager, descriptor);
245 return content != null && contentManager.removeContent(content, true);
248 public void showRunContent(@NotNull final Executor executor, final RunContentDescriptor descriptor) {
249 if(ApplicationManager.getApplication().isUnitTestMode()) return;
251 final ContentManager contentManager = getContentManagerForRunner(executor);
252 RunContentDescriptor oldDescriptor = chooseReuseContentForDescriptor(contentManager, descriptor);
254 final Content content;
256 if(oldDescriptor != null) {
257 content = oldDescriptor.getAttachedContent();
258 myEventDispatcher.getMulticaster().contentRemoved(oldDescriptor, executor.getToolWindowId());
259 oldDescriptor.dispose(); // is of the same category, can be reused
261 else {
262 content = createNewContent(contentManager, descriptor, executor.getToolWindowId());
263 final Icon icon = descriptor.getIcon();
264 content.setIcon(icon == null ? executor.getToolWindowIcon() : icon);
267 content.setComponent(descriptor.getComponent());
268 content.putUserData(DESCRIPTOR_KEY, descriptor);
269 final ProcessHandler processHandler = descriptor.getProcessHandler();
270 if (processHandler != null) {
271 final ProcessAdapter processAdapter = new ProcessAdapter() {
272 public void startNotified(final ProcessEvent event) {
273 LaterInvocator.invokeLater(new Runnable() {
274 public void run() {
275 final Icon icon = descriptor.getIcon();
276 content.setIcon(icon == null ? executor.getToolWindowIcon() : icon);
281 public void processTerminated(final ProcessEvent event) {
282 LaterInvocator.invokeLater(new Runnable() {
283 public void run() {
284 final Icon icon = descriptor.getIcon();
285 content.setIcon(icon == null ? executor.getDisabledIcon() : IconLoader.getTransparentIcon(icon));
290 processHandler.addProcessListener(processAdapter);
291 final Disposable disposer = content.getDisposer();
292 if (disposer != null) {
293 Disposer.register(disposer, new Disposable() {
294 public void dispose() {
295 processHandler.removeProcessListener(processAdapter);
300 content.setDisplayName(descriptor.getDisplayName());
301 descriptor.setAttachedContent(content);
302 content.getManager().setSelectedContent(content);
304 ApplicationManager.getApplication().invokeLater(new Runnable() {
305 public void run() {
306 final ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(executor.getToolWindowId());
307 toolWindow.show(null);
312 @Nullable
313 public RunContentDescriptor getReuseContent(final Executor requestor, DataContext dataContext) {
314 if(ApplicationManager.getApplication().isUnitTestMode()) return null;
315 RunContentDescriptor runContentDescriptor = (RunContentDescriptor)dataContext.getData(GenericProgramRunner.CONTENT_TO_REUSE);
317 if(runContentDescriptor != null) return runContentDescriptor;
319 final ContentManager contentManager = getContentManagerForRunner(requestor);
321 return chooseReuseContentForDescriptor(contentManager, runContentDescriptor);
324 public RunContentDescriptor findContentDescriptor(final Executor requestor, final ProcessHandler handler) {
325 return getDescriptorBy(handler, requestor);
328 public void showRunContent(@NotNull final Executor info, RunContentDescriptor descriptor, RunContentDescriptor contentToReuse) {
329 if(contentToReuse != null) {
330 descriptor.setAttachedContent(contentToReuse.getAttachedContent());
332 showRunContent(info, descriptor);
335 @Nullable
336 private static RunContentDescriptor chooseReuseContentForDescriptor(final ContentManager contentManager, final RunContentDescriptor descriptor) {
337 Content content = null;
338 if (descriptor != null) {
339 final Content attachedContent = descriptor.getAttachedContent();
340 if (attachedContent != null && attachedContent.isValid()) content = attachedContent;
342 if (content == null) {
343 content = contentManager.getSelectedContent();
344 if (content != null && content.isPinned()) content = null;
346 if (content == null || !isTerminated(content)) {
347 return null;
349 final RunContentDescriptor oldDescriptor = getRunContentDescriptorByContent(content);
350 if (oldDescriptor != null && !oldDescriptor.isContentReuseProhibited()) {
351 return oldDescriptor;
354 return null;
357 private ContentManager getContentManagerForRunner(final Executor executor) {
358 final ContentManager contentManager = myToolwindowIdToContentManagerMap.get(executor.getToolWindowId());
359 if (contentManager == null) {
360 LOG.error("Runner " + executor.getId() + " is not registered");
362 return contentManager;
365 private Content createNewContent(final ContentManager contentManager, final RunContentDescriptor descriptor, String toolWindowId) {
366 final String processDisplayName = descriptor.getDisplayName();
367 final Content content = ContentFactory.SERVICE.getInstance().createContent(descriptor.getComponent(), processDisplayName, true);
368 content.putUserData(DESCRIPTOR_KEY, descriptor);
369 content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
370 contentManager.addContent(content);
371 new CloseListener(content, toolWindowId);
372 return content;
375 private static boolean isTerminated(final Content content) {
376 final RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
377 if (descriptor == null) {
378 return true;
380 else {
381 final ProcessHandler processHandler = descriptor.getProcessHandler();
382 return processHandler == null || processHandler.isProcessTerminated();
386 private static RunContentDescriptor getRunContentDescriptorByContent(final Content content) {
387 return content.getUserData(DESCRIPTOR_KEY);
390 @Nullable
391 private static Content getRunContentByDescriptor(final ContentManager contentManager, final RunContentDescriptor descriptor) {
392 final Content[] contents = contentManager.getContents();
393 for (final Content content : contents) {
394 if (descriptor.equals(content.getUserData(DESCRIPTOR_KEY))) {
395 return content;
398 return null;
401 public void addRunContentListener(final RunContentListener listener, final Executor executor) {
402 myEventDispatcher.addListener(new MyRunContentListener() {
403 public void contentSelected(RunContentDescriptor descriptor, String toolwindowId) {
404 if(toolwindowId.equals(executor.getToolWindowId())) {
405 listener.contentSelected(descriptor);
409 public void contentRemoved(RunContentDescriptor descriptor, String toolwindowId) {
410 if(toolwindowId.equals(executor.getToolWindowId())) {
411 listener.contentRemoved(descriptor);
417 public void addRunContentListener(final RunContentListener listener) {
418 MyRunContentListener windowedListener = new MyRunContentListener() {
419 public void contentSelected(RunContentDescriptor descriptor, String ToolwindowId) {
420 listener.contentSelected(descriptor);
423 public void contentRemoved(RunContentDescriptor descriptor, String ToolwindowId) {
424 listener.contentRemoved(descriptor);
427 myEventDispatcher.addListener(windowedListener);
428 myListeners.put(listener, windowedListener);
431 public RunContentDescriptor[] getAllDescriptors() {
432 final List<RunContentDescriptor> descriptors = new ArrayList<RunContentDescriptor>();
433 final String[] ids = myToolwindowIdToContentManagerMap.keySet().toArray(new String[myToolwindowIdToContentManagerMap.size()]);
434 for (String id : ids) {
435 final ContentManager contentManager = myToolwindowIdToContentManagerMap.get(id);
436 final Content[] contents = contentManager.getContents();
437 for (final Content content : contents) {
438 final RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
439 if (descriptor != null) {
440 descriptors.add(descriptor);
445 return descriptors.toArray(new RunContentDescriptor[descriptors.size()]);
448 public void removeRunContentListener(final RunContentListener listener) {
449 MyRunContentListener contentListener = myListeners.remove(listener);
450 myEventDispatcher.removeListener(contentListener);
453 @Nullable
454 private RunContentDescriptor getDescriptorBy(ProcessHandler handler, Executor runnerInfo) {
455 ContentManager contentManager = getContentManagerForRunner(runnerInfo);
456 Content[] contents = contentManager.getContents();
457 for (Content content : contents) {
458 RunContentDescriptor runContentDescriptor = content.getUserData(DESCRIPTOR_KEY);
459 if (runContentDescriptor.getProcessHandler() == handler) {
460 return runContentDescriptor;
463 return null;
466 private class CloseListener extends ContentManagerAdapter implements ProjectManagerListener {
467 private Content myContent;
468 private final String myToolwindowId;
470 private CloseListener(final Content content, String toolWindowId) {
471 myContent = content;
472 content.getManager().addContentManagerListener(this);
473 ProjectManager.getInstance().addProjectManagerListener(this);
474 myToolwindowId = toolWindowId;
477 public void contentRemoved(final ContentManagerEvent event) {
478 final Content content = event.getContent();
479 if (content == myContent) {
480 dispose();
484 private void dispose() {
485 if (myContent == null) return;
487 final Content content = myContent;
488 try {
489 final RunContentDescriptor descriptor = getRunContentDescriptorByContent(content);
491 myEventDispatcher.getMulticaster().contentRemoved(descriptor, myToolwindowId);
493 descriptor.dispose();
495 finally {
496 content.getManager().removeContentManagerListener(this);
497 ProjectManager.getInstance().removeProjectManagerListener(this);
498 content.release(); // don't invoke myContent.release() because myContent becomes null after destroyProcess()
499 myContent = null;
503 public void contentRemoveQuery(final ContentManagerEvent event) {
504 if (event.getContent() == myContent) {
505 final boolean canClose = closeQuery();
506 if (!canClose) {
507 event.consume();
512 public void projectOpened(final Project project) {
515 public void projectClosed(final Project project) {
516 if (myContent != null && project == myProject) {
517 myContent.getManager().removeContent(myContent, true);
518 dispose(); // Dispose content even if content manager refused to.
522 public boolean canCloseProject(final Project project) {
523 if (project != myProject) return true;
525 if (myContent == null) return true;
527 final boolean canClose = closeQuery();
528 if (canClose) {
529 myContent.getManager().removeContent(myContent, true);
530 myContent = null;
532 return canClose;
535 public void projectClosing(final Project project) {
538 private boolean closeQuery() {
539 final RunContentDescriptor descriptor = getRunContentDescriptorByContent(myContent);
541 if (descriptor == null) {
542 return true;
545 final ProcessHandler processHandler = descriptor.getProcessHandler();
546 if (processHandler == null || processHandler.isProcessTerminated()) {
547 return true;
549 final boolean destroyProcess;
550 if (Boolean.TRUE.equals(processHandler.getUserData(ProcessHandler.SILENTLY_DESTROY_ON_CLOSE))) {
551 destroyProcess = true;
553 else {
554 final TerminateRemoteProcessDialog terminateDialog = new TerminateRemoteProcessDialog(myProject, descriptor.getDisplayName(),
555 processHandler.detachIsDefault());
556 terminateDialog.show();
557 if (terminateDialog.getExitCode() != DialogWrapper.OK_EXIT_CODE) return false;
558 destroyProcess = terminateDialog.forceTermination();
560 if (destroyProcess) {
561 processHandler.destroyProcess();
563 else {
564 processHandler.detachProcess();
566 waitForProcess(descriptor);
567 return true;
571 private void waitForProcess(final RunContentDescriptor descriptor) {
572 String progressTitle = ExecutionBundle.message("terminating.process.progress.title", descriptor.getDisplayName());
574 ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
575 private ProgressIndicator myProgressIndicator;
576 private final Semaphore mySemaphore = new Semaphore();
578 private final Runnable myWaitThread = new Runnable() {
579 public void run() {
580 descriptor.getProcessHandler().waitFor();
581 mySemaphore.up();
585 private final Runnable myCancelListener = new Runnable() {
586 public void run() {
587 while(true) {
588 if(myProgressIndicator != null && (myProgressIndicator.isCanceled() || !myProgressIndicator.isRunning())) {
589 mySemaphore.up();
590 break;
592 try {
593 synchronized (this) {
594 wait(2000);
597 catch (InterruptedException e) {
603 public void run() {
604 myProgressIndicator = ProgressManager.getInstance().getProgressIndicator();
605 if (myProgressIndicator != null) {
606 myProgressIndicator.setText(ExecutionBundle.message("waiting.for.vm.detach.progress.text"));
609 ApplicationManager.getApplication().executeOnPooledThread(myWaitThread);
610 ApplicationManager.getApplication().executeOnPooledThread(myCancelListener);
612 mySemaphore.down();
613 mySemaphore.waitFor();
615 }, progressTitle, true, myProject);
618 public interface MyRunContentListener extends EventListener {
619 void contentSelected(RunContentDescriptor descriptor, String toolwindowId);
620 void contentRemoved (RunContentDescriptor descriptor, String toolwindowId);