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
;
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
) {
70 myEventDispatcher
= EventDispatcher
.create(MyRunContentListener
.class);
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() {
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) {
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
) {
135 if(DataConstants
.HELP_ID
.equals(dataId
)) {
136 return executor
.getHelpId();
139 return myInsideGetData
== 1 ? DataManager
.getInstance().getDataContext(contentManager
.getComponent()).getData(dataId
) : null;
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) {
171 toFrontRunContent(requestor
, descriptor
);
175 public void toFrontRunContent(final Executor requestor
, final RunContentDescriptor descriptor
) {
176 ApplicationManager
.getApplication().invokeLater(new Runnable() {
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() {
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);
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
;
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
;
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
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() {
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() {
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() {
306 final ToolWindow toolWindow
= ToolWindowManager
.getInstance(myProject
).getToolWindow(executor
.getToolWindowId());
307 toolWindow
.show(null);
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
);
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
)) {
349 final RunContentDescriptor oldDescriptor
= getRunContentDescriptorByContent(content
);
350 if (oldDescriptor
!= null && !oldDescriptor
.isContentReuseProhibited()) {
351 return oldDescriptor
;
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
);
375 private static boolean isTerminated(final Content content
) {
376 final RunContentDescriptor descriptor
= getRunContentDescriptorByContent(content
);
377 if (descriptor
== null) {
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
);
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
))) {
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
);
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
;
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
) {
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
) {
484 private void dispose() {
485 if (myContent
== null) return;
487 final Content content
= myContent
;
489 final RunContentDescriptor descriptor
= getRunContentDescriptorByContent(content
);
491 myEventDispatcher
.getMulticaster().contentRemoved(descriptor
, myToolwindowId
);
493 descriptor
.dispose();
496 content
.getManager().removeContentManagerListener(this);
497 ProjectManager
.getInstance().removeProjectManagerListener(this);
498 content
.release(); // don't invoke myContent.release() because myContent becomes null after destroyProcess()
503 public void contentRemoveQuery(final ContentManagerEvent event
) {
504 if (event
.getContent() == myContent
) {
505 final boolean canClose
= closeQuery();
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();
529 myContent
.getManager().removeContent(myContent
, true);
535 public void projectClosing(final Project project
) {
538 private boolean closeQuery() {
539 final RunContentDescriptor descriptor
= getRunContentDescriptorByContent(myContent
);
541 if (descriptor
== null) {
545 final ProcessHandler processHandler
= descriptor
.getProcessHandler();
546 if (processHandler
== null || processHandler
.isProcessTerminated()) {
549 final boolean destroyProcess
;
550 if (Boolean
.TRUE
.equals(processHandler
.getUserData(ProcessHandler
.SILENTLY_DESTROY_ON_CLOSE
))) {
551 destroyProcess
= true;
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();
564 processHandler
.detachProcess();
566 waitForProcess(descriptor
);
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() {
580 descriptor
.getProcessHandler().waitFor();
585 private final Runnable myCancelListener
= new Runnable() {
588 if(myProgressIndicator
!= null && (myProgressIndicator
.isCanceled() || !myProgressIndicator
.isRunning())) {
593 synchronized (this) {
597 catch (InterruptedException e
) {
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
);
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
);