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
.openapi
.wm
.impl
;
18 import com
.intellij
.Patches
;
19 import com
.intellij
.ide
.IdeEventQueue
;
20 import com
.intellij
.ide
.ui
.LafManager
;
21 import com
.intellij
.ide
.ui
.LafManagerListener
;
22 import com
.intellij
.openapi
.Disposable
;
23 import com
.intellij
.openapi
.application
.Application
;
24 import com
.intellij
.openapi
.application
.ApplicationAdapter
;
25 import com
.intellij
.openapi
.application
.ApplicationManager
;
26 import com
.intellij
.openapi
.application
.ModalityState
;
27 import com
.intellij
.openapi
.components
.ProjectComponent
;
28 import com
.intellij
.openapi
.diagnostic
.Logger
;
29 import com
.intellij
.openapi
.extensions
.Extensions
;
30 import com
.intellij
.openapi
.fileEditor
.ex
.FileEditorManagerEx
;
31 import com
.intellij
.openapi
.project
.DumbAware
;
32 import com
.intellij
.openapi
.project
.DumbAwareRunnable
;
33 import com
.intellij
.openapi
.project
.DumbService
;
34 import com
.intellij
.openapi
.project
.Project
;
35 import com
.intellij
.openapi
.startup
.StartupManager
;
36 import com
.intellij
.openapi
.ui
.MessageType
;
37 import com
.intellij
.openapi
.ui
.popup
.Balloon
;
38 import com
.intellij
.openapi
.ui
.popup
.JBPopupFactory
;
39 import com
.intellij
.openapi
.util
.*;
40 import com
.intellij
.openapi
.util
.registry
.Registry
;
41 import com
.intellij
.openapi
.wm
.*;
42 import com
.intellij
.openapi
.wm
.ex
.ToolWindowEx
;
43 import com
.intellij
.openapi
.wm
.ex
.ToolWindowManagerEx
;
44 import com
.intellij
.openapi
.wm
.ex
.ToolWindowManagerListener
;
45 import com
.intellij
.openapi
.wm
.ex
.WindowManagerEx
;
46 import com
.intellij
.openapi
.wm
.impl
.commands
.*;
47 import com
.intellij
.ui
.awt
.RelativePoint
;
48 import com
.intellij
.util
.Alarm
;
49 import com
.intellij
.util
.ArrayUtil
;
50 import com
.intellij
.util
.containers
.CollectionFactory
;
51 import com
.intellij
.util
.containers
.HashMap
;
52 import com
.intellij
.util
.containers
.HashSet
;
53 import com
.intellij
.util
.ui
.UIUtil
;
54 import com
.intellij
.util
.ui
.update
.UiNotifyConnector
;
55 import org
.jdom
.Element
;
56 import org
.jetbrains
.annotations
.NonNls
;
57 import org
.jetbrains
.annotations
.NotNull
;
58 import org
.jetbrains
.annotations
.Nullable
;
61 import javax
.swing
.event
.EventListenerList
;
62 import javax
.swing
.event
.HyperlinkListener
;
64 import java
.awt
.event
.FocusEvent
;
65 import java
.awt
.event
.KeyEvent
;
66 import java
.beans
.PropertyChangeEvent
;
67 import java
.beans
.PropertyChangeListener
;
68 import java
.lang
.ref
.WeakReference
;
70 import java
.util
.List
;
73 * @author Anton Katilin
74 * @author Vladimir Kondratyev
76 public final class ToolWindowManagerImpl
extends ToolWindowManagerEx
implements ProjectComponent
, JDOMExternalizable
{
77 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.wm.impl.ToolWindowManagerImpl");
79 private final Project myProject
;
80 private final WindowManagerEx myWindowManager
;
81 private final EventListenerList myListenerList
;
82 private final DesktopLayout myLayout
;
83 private final HashMap
<String
, InternalDecorator
> myId2InternalDecorator
;
84 private final HashMap
<String
, FloatingDecorator
> myId2FloatingDecorator
;
85 private final HashMap
<String
, StripeButton
> myId2StripeButton
;
86 private final HashMap
<String
, FocusWatcher
> myId2FocusWatcher
;
87 private final Set
<String
> myDumbAwareIds
= Collections
.synchronizedSet(CollectionFactory
.<String
>newTroveSet());
89 private final EditorComponentFocusWatcher myEditorComponentFocusWatcher
;
90 private final MyToolWindowPropertyChangeListener myToolWindowPropertyChangeListener
;
91 private final InternalDecoratorListener myInternalDecoratorListener
;
93 private boolean myEditorComponentActive
;
94 private final ActiveStack myActiveStack
;
95 private final SideStack mySideStack
;
97 private ToolWindowsPane myToolWindowsPane
;
98 private IdeFrameImpl myFrame
;
99 private DesktopLayout myLayoutToRestoreLater
= null;
100 @NonNls private static final String EDITOR_ELEMENT
= "editor";
101 @NonNls private static final String ACTIVE_ATTR_VALUE
= "active";
102 @NonNls private static final String FRAME_ELEMENT
= "frame";
103 @NonNls private static final String X_ATTR
= "x";
104 @NonNls private static final String Y_ATTR
= "y";
105 @NonNls private static final String WIDTH_ATTR
= "width";
106 @NonNls private static final String HEIGHT_ATTR
= "height";
107 @NonNls private static final String EXTENDED_STATE_ATTR
= "extended-state";
109 private final EdtAlarm myFocusedComponentAlaram
;
110 private final EdtAlarm myForcedFocusRequestsAlarm
;
112 private final EdtAlarm myIdleAlarm
;
113 private final Set
<Runnable
> myIdleRequests
= new HashSet
<Runnable
>();
114 private final EdtRunnable myIdleRunnable
= new EdtRunnable() {
115 public void runEdt() {
116 if (isFocusTransferReady() && !isIdleQueueEmpty()) {
125 private FocusCommand myRequestFocusCmd
;
126 private final ArrayList
<FocusCommand
> myFocusRequests
= new ArrayList
<FocusCommand
>();
128 private final ArrayList
<KeyEvent
> myToDispatchOnDone
= new ArrayList
<KeyEvent
>();
129 private int myFlushingIdleRequestsEntryCount
= 0;
131 private WeakReference
<FocusCommand
> myLastForcedRequest
= new WeakReference
<FocusCommand
>(null);
133 private FocusCommand myFocusCommandOnAppActivation
;
134 private ActionCallback myCallbackOnActivation
;
135 private WeakReference
<Component
> myFocusedComponentOnDeactivation
;
136 private WeakReference
<Component
> myLastFocusedProjectComponent
;
138 private final IdeEventQueue myQueue
;
139 private final KeyProcessorConext myKeyProcessorContext
= new KeyProcessorConext();
141 private long myCmdTimestamp
;
142 private long myForcedCmdTimestamp
;
143 private final Application myApp
;
145 private Set
<String
> myRestoredToolWindowIds
= new java
.util
.HashSet
<String
>();
148 * invoked by reflection
150 public ToolWindowManagerImpl(final Project project
, WindowManagerEx windowManagerEx
, final Application app
) {
152 myQueue
= IdeEventQueue
.getInstance();
154 myWindowManager
= windowManagerEx
;
155 myListenerList
= new EventListenerList();
157 myLayout
= new DesktopLayout();
158 myLayout
.copyFrom(windowManagerEx
.getLayout());
160 myId2InternalDecorator
= new HashMap
<String
, InternalDecorator
>();
161 myId2FloatingDecorator
= new HashMap
<String
, FloatingDecorator
>();
162 myId2StripeButton
= new HashMap
<String
, StripeButton
>();
163 myId2FocusWatcher
= new HashMap
<String
, FocusWatcher
>();
165 myEditorComponentFocusWatcher
= new EditorComponentFocusWatcher();
166 myToolWindowPropertyChangeListener
= new MyToolWindowPropertyChangeListener();
167 myInternalDecoratorListener
= new MyInternalDecoratorListener();
169 myEditorComponentActive
= false;
170 myActiveStack
= new ActiveStack();
171 mySideStack
= new SideStack();
173 myFocusedComponentAlaram
= new EdtAlarm(project
);
174 myForcedFocusRequestsAlarm
= new EdtAlarm(project
);
175 myIdleAlarm
= new EdtAlarm(project
);
177 final AppListener myAppListener
= new AppListener();
178 app
.addApplicationListener(myAppListener
);
179 Disposer
.register(project
, new Disposable() {
180 public void dispose() {
181 app
.removeApplicationListener(myAppListener
);
185 IdeEventQueue
.getInstance().addDispatcher(new IdeEventQueue
.EventDispatcher() {
186 public boolean dispatch(AWTEvent e
) {
187 if (e
instanceof FocusEvent
) {
188 final FocusEvent fe
= (FocusEvent
)e
;
189 final Component c
= fe
.getComponent();
190 final IdeFrameImpl frame
= myWindowManager
.getFrame(myProject
);
191 if (c
instanceof Window
|| c
== null || frame
== null) return false;
192 if (isProjectComponent(c
)) {
193 if (fe
.getID() == FocusEvent
.FOCUS_GAINED
) {
194 myLastFocusedProjectComponent
= new WeakReference
<Component
>(c
);
204 private Component
getLastFocusedProjectComponent() {
205 return myLastFocusedProjectComponent
!= null ? myLastFocusedProjectComponent
.get() : null;
208 public Project
getProject() {
212 public void initComponent() {
215 public void disposeComponent() {
218 public void projectOpened() {
219 final MyUIManagerPropertyChangeListener myUIManagerPropertyChangeListener
= new MyUIManagerPropertyChangeListener();
220 final MyLafManagerListener myLafManagerListener
= new MyLafManagerListener();
222 UIManager
.addPropertyChangeListener(myUIManagerPropertyChangeListener
);
223 LafManager
.getInstance().addLafManagerListener(myLafManagerListener
);
225 Disposer
.register(myProject
, new Disposable() {
226 public void dispose() {
227 UIManager
.removePropertyChangeListener(myUIManagerPropertyChangeListener
);
228 LafManager
.getInstance().removeLafManagerListener(myLafManagerListener
);
231 myFrame
= myWindowManager
.allocateFrame(myProject
);
232 LOG
.assertTrue(myFrame
!= null);
234 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
236 myToolWindowsPane
= new ToolWindowsPane(myFrame
, this);
237 ((IdeRootPane
)myFrame
.getRootPane()).setToolWindowsPane(myToolWindowsPane
);
238 appendUpdateToolWindowsPaneCmd(commandsList
);
240 myFrame
.setTitle(FrameTitleBuilder
.getInstance().getProjectTitle(myProject
));
242 final JComponent editorComponent
= FileEditorManagerEx
.getInstanceEx(myProject
).getComponent();
243 myEditorComponentFocusWatcher
.install(editorComponent
);
245 appendSetEditorComponentCmd(editorComponent
, commandsList
);
246 if (myEditorComponentActive
) {
247 activateEditorComponentImpl(commandsList
, true);
249 execute(commandsList
);
251 final DumbService
.DumbModeListener dumbModeListener
= new DumbService
.DumbModeListener() {
252 public void enteredDumbMode() {
253 for (final String id
: getToolWindowIds()) {
254 if (!myDumbAwareIds
.contains(id
)) {
255 if (isToolWindowVisible(id
)) {
256 hideToolWindow(id
, true);
258 getStripeButton(id
).setEnabled(false);
263 public void exitDumbMode() {
264 for (final String id
: getToolWindowIds()) {
265 getStripeButton(id
).setEnabled(true);
269 myProject
.getMessageBus().connect().subscribe(DumbService
.DUMB_MODE
, dumbModeListener
);
271 StartupManager
.getInstance(myProject
).registerPostStartupActivity(new DumbAwareRunnable() {
273 registerToolWindowsFromBeans();
274 if (DumbService
.getInstance(myProject
).isDumb()) {
275 dumbModeListener
.enteredDumbMode();
281 private void registerToolWindowsFromBeans() {
282 ToolWindowEP
[] beans
= Extensions
.getExtensions(ToolWindowEP
.EP_NAME
);
283 for (final ToolWindowEP bean
: beans
) {
284 final Condition condition
= bean
.getCondition();
285 if (condition
!= null && !condition
.value(myProject
)) {
288 ToolWindowAnchor toolWindowAnchor
;
290 toolWindowAnchor
= ToolWindowAnchor
.fromText(bean
.anchor
);
292 catch (Exception e
) {
296 JLabel label
= new JLabel("Initializing...", JLabel
.CENTER
);
297 label
.setOpaque(true);
298 final Color treeBg
= UIManager
.getColor("Tree.background");
299 label
.setBackground(new Color(treeBg
.getRed(), treeBg
.getGreen(), treeBg
.getBlue(), 180));
300 final Color treeFg
= UIManager
.getColor("Tree.foreground");
301 label
.setForeground(new Color(treeFg
.getRed(), treeFg
.getGreen(), treeFg
.getBlue(), 180));
302 final ToolWindowFactory factory
= bean
.getToolWindowFactory();
303 final ToolWindowImpl toolWindow
= (ToolWindowImpl
)registerToolWindow(bean
.id
, label
, toolWindowAnchor
, myProject
, factory
instanceof DumbAware
);
304 toolWindow
.setContentFactory(factory
);
305 if (bean
.icon
!= null) {
306 Icon icon
= IconLoader
.findIcon(bean
.icon
, factory
.getClass());
309 icon
= IconLoader
.getIcon(bean
.icon
);
310 } catch (Exception ignored
) {}
312 toolWindow
.setIcon(icon
);
315 if (!getInfo(bean
.id
).isSplit() && bean
.secondary
&& !myRestoredToolWindowIds
.contains(bean
.id
)) {
316 toolWindow
.setSplitMode(bean
.secondary
, null);
319 final ActionCallback activation
= toolWindow
.setActivation(new ActionCallback());
321 UiNotifyConnector
.doWhenFirstShown(label
, new Runnable() {
323 ApplicationManager
.getApplication().invokeLater(new DumbAwareRunnable() {
325 toolWindow
.ensureContentInitialized();
326 activation
.setDone();
334 public void projectClosed() {
335 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
336 final String
[] ids
= getToolWindowIds();
338 // Remove ToolWindowsPane
340 ((IdeRootPane
)myFrame
.getRootPane()).setToolWindowsPane(null);
341 myWindowManager
.releaseFrame(myFrame
);
342 appendUpdateToolWindowsPaneCmd(commandsList
);
344 // Hide all tool windows
346 for (final String id
: ids
) {
347 deactivateToolWindowImpl(id
, true, commandsList
);
350 // Remove editor component
352 final JComponent editorComponent
= FileEditorManagerEx
.getInstanceEx(myProject
).getComponent();
353 myEditorComponentFocusWatcher
.deinstall(editorComponent
);
354 appendSetEditorComponentCmd(null, commandsList
);
355 execute(commandsList
);
358 public void addToolWindowManagerListener(final ToolWindowManagerListener l
) {
359 myListenerList
.add(ToolWindowManagerListener
.class, l
);
362 public void removeToolWindowManagerListener(final ToolWindowManagerListener l
) {
363 myListenerList
.remove(ToolWindowManagerListener
.class, l
);
367 * This is helper method. It delegated its fuctionality to the WindowManager.
368 * Before delegating it fires state changed.
370 private void execute(final ArrayList
<FinalizableCommand
> commandList
) {
371 for (FinalizableCommand each
: commandList
) {
372 if (each
.willChangeState()) {
378 for (FinalizableCommand each
: commandList
) {
379 each
.beforeExecute(this);
381 myWindowManager
.getCommandProcessor().execute(commandList
, myProject
.getDisposed());
384 public void activateEditorComponent() {
385 activateEditorComponent(true);
388 private void activateEditorComponent(boolean forced
) {
389 if (LOG
.isDebugEnabled()) {
390 LOG
.debug("enter: activateEditorComponent()");
392 ApplicationManager
.getApplication().assertIsDispatchThread();
393 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
394 activateEditorComponentImpl(commandList
, forced
);
395 execute(commandList
);
398 private void activateEditorComponentImpl(final ArrayList
<FinalizableCommand
> commandList
, final boolean forced
) {
399 final String active
= getActiveToolWindowId();
400 // Now we have to request focus into most recent focused editor
401 appendRequestFocusInEditorComponentCmd(commandList
, forced
).doWhenDone(new Runnable() {
403 final ArrayList
<FinalizableCommand
> postExecute
= new ArrayList
<FinalizableCommand
>();
405 if (LOG
.isDebugEnabled()) {
406 LOG
.debug("editor activated");
408 deactivateWindows(postExecute
, null);
409 myActiveStack
.clear();
410 myEditorComponentActive
= true;
412 execute(postExecute
);
414 }).doWhenRejected(new Runnable() {
417 requestFocus(new FocusCommand() {
418 public ActionCallback
run() {
419 final ArrayList
<FinalizableCommand
> cmds
= new ArrayList
<FinalizableCommand
>();
421 final WindowInfoImpl toReactivate
= getInfo(active
);
422 final boolean reactivateLastActive
= toReactivate
!= null && !isToHide(toReactivate
);
423 deactivateWindows(cmds
, reactivateLastActive ? active
: null);
426 if (reactivateLastActive
) {
427 activateToolWindow(active
, false, true);
430 if (active
!= null) {
431 myActiveStack
.remove(active
, false);
434 if (!myActiveStack
.isEmpty()) {
435 activateToolWindow(myActiveStack
.peek(), false, true);
438 return new ActionCallback
.Done();
446 private void deactivateWindows(final ArrayList
<FinalizableCommand
> postExecute
, String idToIgnore
) {
447 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
448 for (final WindowInfoImpl info
: infos
) {
449 final boolean shouldHide
= isToHide(info
);
450 if (idToIgnore
!= null && idToIgnore
.equals(info
.getId())) {
453 deactivateToolWindowImpl(info
.getId(), shouldHide
, postExecute
);
457 private boolean isToHide(final WindowInfoImpl info
) {
458 return (info
.isAutoHide() || info
.isSliding()) && !(info
.isFloating() && hasModalChild(info
));
462 * Helper method. It makes window visible, activates it and request focus into the tool window.
463 * But it doesn't deactivate other tool windows. Use <code>prepareForActivation</code> method to
464 * deactivates other tool windows.
466 * @param dirtyMode if <code>true</code> then all UI operations are performed in "dirty" mode.
467 * It means that UI isn't validated and repainted just after each add/remove operation.
468 * @see ToolWindowManagerImpl#prepareForActivation
470 private void showAndActivate(final String id
,
471 final boolean dirtyMode
,
472 final ArrayList
<FinalizableCommand
> commandsList
,
473 boolean autoFocusContents
) {
474 if (!getToolWindow(id
).isAvailable()) {
478 final WindowInfoImpl info
= getInfo(id
);
479 boolean toApplyInfo
= false;
480 if (!info
.isActive()) {
481 info
.setActive(true);
484 showToolWindowImpl(id
, dirtyMode
, commandsList
);
488 appendApplyWindowInfoCmd(info
, commandsList
);
489 myActiveStack
.push(id
);
490 myEditorComponentActive
= false;
493 if (autoFocusContents
) {
494 appendRequestFocusInToolWindowCmd(id
, commandsList
, true);
498 void activateToolWindow(final String id
, boolean forced
, boolean autoFocusContents
) {
499 if (LOG
.isDebugEnabled()) {
500 LOG
.debug("enter: activateToolWindow(" + id
+ ")");
502 ApplicationManager
.getApplication().assertIsDispatchThread();
504 if (DumbService
.getInstance(myProject
).isDumb() && !myDumbAwareIds
.contains(id
)) {
508 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
509 activateToolWindowImpl(id
, commandList
, forced
, autoFocusContents
);
510 execute(commandList
);
513 private void activateToolWindowImpl(final String id
,
514 final ArrayList
<FinalizableCommand
> commandList
,
516 boolean autoFocusContents
) {
517 if (!isUnforcedRequestAllowed() && !forced
) return;
519 if (LOG
.isDebugEnabled()) {
520 LOG
.debug("enter: activateToolWindowImpl(" + id
+ ")");
522 if (!getToolWindow(id
).isAvailable()) {
523 // Tool window can be "logically" active but not focused. For example,
524 // when the user switched to another application. So we just need to bring
525 // tool window's window to front.
526 final InternalDecorator decorator
= getInternalDecorator(id
);
527 if (!decorator
.hasFocus() && autoFocusContents
) {
528 appendRequestFocusInToolWindowCmd(id
, commandList
, forced
);
532 prepareForActivation(id
, commandList
);
533 showAndActivate(id
, false, commandList
, autoFocusContents
);
537 * Checkes whether the specified <code>id</code> defines installed tool
538 * window. If it's not then throws <code>IllegalStateException</code>.
540 * @throws IllegalStateException if tool window isn't installed.
542 private void checkId(final String id
) {
543 if (!myLayout
.isToolWindowRegistered(id
)) {
544 throw new IllegalStateException("window with id=\"" + id
+ "\" isn't registered");
549 * Helper method. It deactivates (and hides) window with specified <code>id</code>.
551 * @param id <code>id</code> of the tool window to be deactivated.
552 * @param shouldHide if <code>true</code> then also hides specified tool window.
554 private void deactivateToolWindowImpl(final String id
, final boolean shouldHide
, final List
<FinalizableCommand
> commandsList
) {
555 if (LOG
.isDebugEnabled()) {
556 LOG
.debug("enter: deactivateToolWindowImpl(" + id
+ "," + shouldHide
+ ")");
558 final WindowInfoImpl info
= getInfo(id
);
559 if (shouldHide
&& info
.isVisible()) {
560 info
.setVisible(false);
561 if (info
.isFloating()) {
562 appendRemoveFloatingDecoratorCmd(info
, commandsList
);
564 else { // docked and sliding windows
565 appendRemoveDecoratorCmd(id
, false, commandsList
);
568 info
.setActive(false);
569 appendApplyWindowInfoCmd(info
, commandsList
);
572 public String
[] getToolWindowIds() {
573 ApplicationManager
.getApplication().assertIsDispatchThread();
574 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
575 final String
[] ids
= ArrayUtil
.newStringArray(infos
.length
);
576 for (int i
= 0; i
< infos
.length
; i
++) {
577 ids
[i
] = infos
[i
].getId();
582 public String
getActiveToolWindowId() {
583 ApplicationManager
.getApplication().assertIsDispatchThread();
584 return myLayout
.getActiveId();
587 public String
getLastActiveToolWindowId() {
588 return getLastActiveToolWindowId(null);
591 public String
getLastActiveToolWindowId(Condition
<JComponent
> condition
) {
592 ApplicationManager
.getApplication().assertIsDispatchThread();
593 String lastActiveToolWindowId
= null;
594 for (int i
= 0; i
< myActiveStack
.getPersistentSize(); i
++) {
595 final String id
= myActiveStack
.peekPersistent(i
);
596 final ToolWindow toolWindow
= getToolWindow(id
);
597 LOG
.assertTrue(toolWindow
!= null);
598 if (toolWindow
.isAvailable()) {
599 if (condition
== null || condition
.value(toolWindow
.getComponent())) {
600 lastActiveToolWindowId
= id
;
605 return lastActiveToolWindowId
;
609 * @return floating decorator for the tool window with specified <code>ID</code>.
611 private FloatingDecorator
getFloatingDecorator(final String id
) {
612 return myId2FloatingDecorator
.get(id
);
616 * @return internal decorator for the tool window with specified <code>ID</code>.
618 private InternalDecorator
getInternalDecorator(final String id
) {
619 return myId2InternalDecorator
.get(id
);
623 * @return tool button for the window with specified <code>ID</code>.
625 private StripeButton
getStripeButton(final String id
) {
626 return myId2StripeButton
.get(id
);
630 * @return info for the tool window with specified <code>ID</code>.
632 private WindowInfoImpl
getInfo(final String id
) {
633 return myLayout
.getInfo(id
, true);
636 public List
<String
> getIdsOn(final ToolWindowAnchor anchor
) {
637 return myLayout
.getVisibleIdsOn(anchor
, this);
640 public ToolWindow
getToolWindow(final String id
) {
641 ApplicationManager
.getApplication().assertIsDispatchThread();
642 if (!myLayout
.isToolWindowRegistered(id
)) {
645 return getInternalDecorator(id
).getToolWindow();
648 void showToolWindow(final String id
) {
649 if (LOG
.isDebugEnabled()) {
650 LOG
.debug("enter: showToolWindow(" + id
+ ")");
652 ApplicationManager
.getApplication().assertIsDispatchThread();
653 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
654 showToolWindowImpl(id
, false, commandList
);
655 execute(commandList
);
658 public void hideToolWindow(final String id
, final boolean hideSide
) {
659 hideToolWindow(id
, hideSide
, true);
662 public void hideToolWindow(final String id
, final boolean hideSide
, final boolean moveFocus
) {
663 ApplicationManager
.getApplication().assertIsDispatchThread();
665 final WindowInfoImpl info
= getInfo(id
);
666 if (!info
.isVisible()) return;
667 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
668 final boolean wasActive
= info
.isActive();
670 // hide and deactivate
672 deactivateToolWindowImpl(id
, true, commandList
);
674 if (hideSide
|| info
.isFloating()) {
675 final List
<String
> ids
= myLayout
.getVisibleIdsOn(info
.getAnchor(), this);
676 for (String each
: ids
) {
677 myActiveStack
.remove(each
, true);
681 while (!mySideStack
.isEmpty(info
.getAnchor())) {
682 mySideStack
.pop(info
.getAnchor());
685 final String
[] all
= getToolWindowIds();
686 for (String eachId
: all
) {
687 final WindowInfoImpl eachInfo
= getInfo(eachId
);
688 if (eachInfo
.isVisible() && eachInfo
.getAnchor() == info
.getAnchor()) {
689 deactivateToolWindowImpl(eachId
, true, commandList
);
693 activateEditorComponentImpl(commandList
, true);
697 // first of all we have to find tool window that was located at the same side and
700 WindowInfoImpl info2
= null;
701 while (!mySideStack
.isEmpty(info
.getAnchor())) {
702 final WindowInfoImpl storedInfo
= mySideStack
.pop(info
.getAnchor());
703 final WindowInfoImpl currentInfo
= getInfo(storedInfo
.getId());
704 LOG
.assertTrue(currentInfo
!= null);
705 // SideStack contains copies of real WindowInfos. It means that
706 // these stored infos can be invalid. The following loop removes invalid WindowInfos.
707 if (storedInfo
.getAnchor() == currentInfo
.getAnchor() &&
708 storedInfo
.getType() == currentInfo
.getType() &&
709 storedInfo
.isAutoHide() == currentInfo
.isAutoHide()) {
715 showToolWindowImpl(info2
.getId(), false, commandList
);
718 // If we hide currently active tool window then we should activate the previous
719 // one which is located in the tool window stack.
720 // Activate another tool window if no active tool window exists and
721 // window stack is enabled.
723 myActiveStack
.remove(id
, false); // hidden window should be at the top of stack
724 if (wasActive
&& moveFocus
) {
725 if (myActiveStack
.isEmpty()) {
726 activateEditorComponentImpl(commandList
, false);
729 final String toBeActivatedId
= myActiveStack
.pop();
730 if (toBeActivatedId
!= null) {
731 activateToolWindowImpl(toBeActivatedId
, commandList
, false, true);
737 execute(commandList
);
741 * @param dirtyMode if <code>true</code> then all UI operations are performed in dirty mode.
743 private void showToolWindowImpl(final String id
, final boolean dirtyMode
, final List
<FinalizableCommand
> commandsList
) {
744 final WindowInfoImpl toBeShownInfo
= getInfo(id
);
745 if (toBeShownInfo
.isVisible() || !getToolWindow(id
).isAvailable()) {
749 if (DumbService
.getInstance(myProject
).isDumb() && !myDumbAwareIds
.contains(id
)) {
753 toBeShownInfo
.setVisible(true);
754 final InternalDecorator decorator
= getInternalDecorator(id
);
756 if (toBeShownInfo
.isFloating()) {
757 commandsList
.add(new AddFloatingDecoratorCmd(decorator
, toBeShownInfo
));
759 else { // docked and sliding windows
761 // If there is tool window on the same side then we have to hide it, i.e.
762 // clear place for tool window to be shown.
764 // We store WindowInfo of hidden tool window in the SideStack (if the tool window
765 // is docked and not auto-hide one). Therefore it's possible to restore the
766 // hidden tool window when showing tool window will be closed.
768 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
769 for (final WindowInfoImpl info
: infos
) {
770 if (id
.equals(info
.getId())) {
773 if (info
.isVisible() &&
774 info
.getType() == toBeShownInfo
.getType() &&
775 info
.getAnchor() == toBeShownInfo
.getAnchor() &&
776 info
.isSplit() == toBeShownInfo
.isSplit()) {
777 // hide and deactivate tool window
778 info
.setVisible(false);
779 appendRemoveDecoratorCmd(info
.getId(), false, commandsList
);
780 if (info
.isActive()) {
781 info
.setActive(false);
783 appendApplyWindowInfoCmd(info
, commandsList
);
784 // store WindowInfo into the SideStack
785 if (info
.isDocked() && !info
.isAutoHide()) {
786 mySideStack
.push(info
);
790 appendAddDecoratorCmd(decorator
, toBeShownInfo
, dirtyMode
, commandsList
);
792 // Remove tool window from the SideStack.
794 mySideStack
.remove(id
);
797 appendApplyWindowInfoCmd(toBeShownInfo
, commandsList
);
800 public ToolWindow
registerToolWindow(@NotNull final String id
,
801 @NotNull final JComponent component
,
802 @NotNull final ToolWindowAnchor anchor
) {
803 return registerToolWindow(id
, component
, anchor
, false);
806 public ToolWindow
registerToolWindow(@NotNull final String id
,
807 @NotNull JComponent component
,
808 @NotNull ToolWindowAnchor anchor
,
809 Disposable parentDisposable
) {
810 return registerToolWindow(id
, component
, anchor
, parentDisposable
, false);
813 public ToolWindow
registerToolWindow(@NotNull final String id
,
814 @NotNull JComponent component
,
815 @NotNull ToolWindowAnchor anchor
,
816 Disposable parentDisposable
,
817 boolean canWorkInDumbMode
) {
818 return registerDisposable(id
, parentDisposable
, registerToolWindow(id
, component
, anchor
, canWorkInDumbMode
));
821 private ToolWindow
registerToolWindow(@NotNull final String id
,
822 @NotNull final JComponent component
,
823 @NotNull final ToolWindowAnchor anchor
,
824 boolean canWorkInDumbMode
) {
825 return registerToolWindow(id
, component
, anchor
, false, false, canWorkInDumbMode
);
828 public ToolWindow
registerToolWindow(@NotNull final String id
, final boolean canCloseContent
, @NotNull final ToolWindowAnchor anchor
) {
829 return registerToolWindow(id
, null, anchor
, false, canCloseContent
, false);
832 public ToolWindow
registerToolWindow(@NotNull final String id
,
833 final boolean canCloseContent
,
834 @NotNull final ToolWindowAnchor anchor
,
835 final boolean sideTool
) {
836 return registerToolWindow(id
, null, anchor
, sideTool
, canCloseContent
, false);
840 public ToolWindow
registerToolWindow(@NotNull final String id
,
841 final boolean canCloseContent
,
842 @NotNull final ToolWindowAnchor anchor
,
843 final Disposable parentDisposable
,
844 final boolean canWorkInDumbMode
) {
845 return registerDisposable(id
, parentDisposable
, registerToolWindow(id
, null, anchor
, false, canCloseContent
, canWorkInDumbMode
));
848 private ToolWindow
registerToolWindow(@NotNull final String id
,
849 @Nullable final JComponent component
,
850 @NotNull final ToolWindowAnchor anchor
,
852 boolean canCloseContent
,
853 final boolean canWorkInDumbMode
) {
854 if (LOG
.isDebugEnabled()) {
855 LOG
.debug("enter: installToolWindow(" + id
+ "," + component
+ "," + anchor
+ "\")");
857 ApplicationManager
.getApplication().assertIsDispatchThread();
858 if (myLayout
.isToolWindowRegistered(id
)) {
859 throw new IllegalArgumentException("window with id=\"" + id
+ "\" is already registered");
862 final WindowInfoImpl info
= myLayout
.register(id
, anchor
, sideTool
);
863 final boolean wasActive
= info
.isActive();
864 final boolean wasVisible
= info
.isVisible();
865 info
.setActive(false);
866 info
.setVisible(false);
870 final ToolWindowImpl toolWindow
= new ToolWindowImpl(this, id
, canCloseContent
, component
);
871 final InternalDecorator decorator
= new InternalDecorator(myProject
, info
.copy(), toolWindow
);
872 myId2InternalDecorator
.put(id
, decorator
);
873 decorator
.addInternalDecoratorListener(myInternalDecoratorListener
);
874 toolWindow
.addPropertyChangeListener(myToolWindowPropertyChangeListener
);
875 myId2FocusWatcher
.put(id
, new ToolWindowFocusWatcher(toolWindow
));
877 // Create and show tool button
879 final StripeButton button
= new StripeButton(decorator
, myToolWindowsPane
);
880 myId2StripeButton
.put(id
, button
);
881 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
882 appendAddButtonCmd(button
, info
, commandsList
);
884 if (canWorkInDumbMode
) {
885 myDumbAwareIds
.add(id
);
886 } else if (DumbService
.getInstance(getProject()).isDumb()) {
887 button
.setEnabled(false);
890 // If preloaded info is visible or active then we have to show/activate the installed
891 // tool window. This step has sense only for windows which are not in the autohide
892 // mode. But if tool window was active but its mode doen't allow to activate it again
893 // (for example, tool window is in autohide mode) then we just activate editor component.
895 if (!info
.isAutoHide() && (info
.isDocked() || info
.isFloating())) {
897 activateToolWindowImpl(info
.getId(), commandsList
, true, true);
899 else if (wasVisible
) {
900 showToolWindowImpl(info
.getId(), false, commandsList
);
903 else if (wasActive
) { // tool window was active but it cannot be activate again
904 activateEditorComponentImpl(commandsList
, true);
907 execute(commandsList
);
908 fireToolWindowRegistered(id
);
912 private ToolWindow
registerDisposable(final String id
, final Disposable parentDisposable
, final ToolWindow window
) {
913 Disposer
.register(parentDisposable
, new Disposable() {
914 public void dispose() {
915 unregisterToolWindow(id
);
921 public void unregisterToolWindow(@NotNull final String id
) {
922 if (LOG
.isDebugEnabled()) {
923 LOG
.debug("enter: unregisterToolWindow(" + id
+ ")");
925 ApplicationManager
.getApplication().assertIsDispatchThread();
926 if (!myLayout
.isToolWindowRegistered(id
)) {
930 final WindowInfoImpl info
= getInfo(id
);
931 final ToolWindowEx toolWindow
= (ToolWindowEx
)getToolWindow(id
);
932 // Save recent appearance of tool window
933 myLayout
.unregister(id
);
934 // Remove decorator and tool button from the screen
935 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
936 if (info
.isVisible()) {
937 info
.setVisible(false);
938 if (info
.isFloating()) {
939 appendRemoveFloatingDecoratorCmd(info
, commandsList
);
941 else { // floating and sliding windows
942 appendRemoveDecoratorCmd(id
, false, commandsList
);
945 appendRemoveButtonCmd(id
, commandsList
);
946 appendApplyWindowInfoCmd(info
, commandsList
);
947 execute(commandsList
);
948 // Remove all references on tool window and save its last properties
949 toolWindow
.removePropertyChangeListener(myToolWindowPropertyChangeListener
);
950 myActiveStack
.remove(id
, true);
951 mySideStack
.remove(id
);
952 // Destroy stripe button
953 final StripeButton button
= getStripeButton(id
);
955 myId2StripeButton
.remove(id
);
957 myId2FocusWatcher
.remove(id
);
959 final InternalDecorator decorator
= getInternalDecorator(id
);
961 decorator
.removeInternalDecoratorListener(myInternalDecoratorListener
);
962 myId2InternalDecorator
.remove(id
);
965 public DesktopLayout
getLayout() {
966 ApplicationManager
.getApplication().assertIsDispatchThread();
970 public void setLayoutToRestoreLater(DesktopLayout layout
) {
971 myLayoutToRestoreLater
= layout
;
974 public DesktopLayout
getLayoutToRestoreLater() {
975 return myLayoutToRestoreLater
;
978 public void setLayout(final DesktopLayout layout
) {
979 ApplicationManager
.getApplication().assertIsDispatchThread();
980 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
981 // hide tool window that are invisible in new layout
982 final WindowInfoImpl
[] currentInfos
= myLayout
.getInfos();
983 for (final WindowInfoImpl currentInfo
: currentInfos
) {
984 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
988 if (currentInfo
.isVisible() && !info
.isVisible()) {
989 deactivateToolWindowImpl(currentInfo
.getId(), true, commandList
);
992 // change anchor of tool windows
993 for (final WindowInfoImpl currentInfo
: currentInfos
) {
994 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
998 if (currentInfo
.getAnchor() != info
.getAnchor() || currentInfo
.getOrder() != info
.getOrder()) {
999 setToolWindowAnchorImpl(currentInfo
.getId(), info
.getAnchor(), info
.getOrder(), commandList
);
1002 // change types of tool windows
1003 for (final WindowInfoImpl currentInfo
: currentInfos
) {
1004 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
1008 if (currentInfo
.getType() != info
.getType()) {
1009 setToolWindowTypeImpl(currentInfo
.getId(), info
.getType(), commandList
);
1012 // change auto-hide state
1013 for (final WindowInfoImpl currentInfo
: currentInfos
) {
1014 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
1018 if (currentInfo
.isAutoHide() != info
.isAutoHide()) {
1019 setToolWindowAutoHideImpl(currentInfo
.getId(), info
.isAutoHide(), commandList
);
1022 // restore visibility
1023 for (final WindowInfoImpl currentInfo
: currentInfos
) {
1024 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
1028 if (info
.isVisible()) {
1029 showToolWindowImpl(currentInfo
.getId(), false, commandList
);
1032 // if there is no any active tool window and editor is also inactive
1033 // then activate editor
1034 if (!myEditorComponentActive
&& getActiveToolWindowId() == null) {
1035 activateEditorComponentImpl(commandList
, true);
1037 execute(commandList
);
1040 public void invokeLater(final Runnable runnable
) {
1041 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1042 commandList
.add(new InvokeLaterCmd(runnable
, myWindowManager
.getCommandProcessor()));
1043 execute(commandList
);
1046 public IdeFocusManager
getFocusManager() {
1047 return IdeFocusManager
.getInstance(myProject
);
1051 public void notifyByBalloon(@NotNull final String toolWindowId
, @NotNull final MessageType type
, @NotNull final String htmlBody
) {
1052 notifyByBalloon(toolWindowId
, type
, htmlBody
, null, null);
1055 public void notifyByBalloon(@NotNull final String toolWindowId
,
1056 @NotNull final MessageType type
,
1057 @NotNull final String text
,
1058 @Nullable final Icon icon
,
1059 @Nullable HyperlinkListener listener
) {
1060 checkId(toolWindowId
);
1062 final Stripe stripe
= myToolWindowsPane
.getStripeFor(toolWindowId
);
1063 final ToolWindowImpl window
= getInternalDecorator(toolWindowId
).getToolWindow();
1064 if (!window
.isAvailable()) {
1065 window
.setPlaceholderMode(true);
1066 stripe
.updateState();
1067 stripe
.revalidate();
1071 final ToolWindowAnchor anchor
= getInfo(toolWindowId
).getAnchor();
1072 final Ref
<Balloon
.Position
> position
= Ref
.create(Balloon
.Position
.below
);
1073 if (ToolWindowAnchor
.TOP
== anchor
) {
1074 position
.set(Balloon
.Position
.below
);
1076 else if (ToolWindowAnchor
.BOTTOM
== anchor
) {
1077 position
.set(Balloon
.Position
.above
);
1079 else if (ToolWindowAnchor
.LEFT
== anchor
) {
1080 position
.set(Balloon
.Position
.atRight
);
1082 else if (ToolWindowAnchor
.RIGHT
== anchor
) {
1083 position
.set(Balloon
.Position
.atLeft
);
1086 Icon actualIcon
= icon
!= null ? icon
: type
.getDefaultIcon();
1088 final Balloon balloon
=
1089 JBPopupFactory
.getInstance().createHtmlTextBalloonBuilder(text
.replace("\n", "<br>"), actualIcon
, type
.getPopupBackground(), listener
)
1091 Disposer
.register(balloon
, new Disposable() {
1092 public void dispose() {
1093 window
.setPlaceholderMode(false);
1094 stripe
.updateState();
1095 stripe
.revalidate();
1100 final StripeButton button
= stripe
.getButtonFor(toolWindowId
);
1101 if (button
== null) return;
1103 final Runnable show
= new Runnable() {
1105 if (button
.isShowing()) {
1106 final Point point
= new Point(button
.getBounds().width
/ 2, button
.getHeight() / 2 - 2);
1107 balloon
.show(new RelativePoint(button
, point
), position
.get());
1110 final Rectangle bounds
= myToolWindowsPane
.getBounds();
1111 final Point target
= UIUtil
.getCenterPoint(bounds
, new Dimension(1, 1));
1112 if (ToolWindowAnchor
.TOP
== anchor
) {
1115 else if (ToolWindowAnchor
.BOTTOM
== anchor
) {
1116 target
.y
= bounds
.height
- 3;
1118 else if (ToolWindowAnchor
.LEFT
== anchor
) {
1121 else if (ToolWindowAnchor
.RIGHT
== anchor
) {
1122 target
.x
= bounds
.width
;
1125 balloon
.show(new RelativePoint(myToolWindowsPane
, target
), position
.get());
1130 if (!button
.isValid()) {
1131 SwingUtilities
.invokeLater(new Runnable() {
1143 public boolean isEditorComponentActive() {
1144 ApplicationManager
.getApplication().assertIsDispatchThread();
1145 return myEditorComponentActive
;
1148 ToolWindowAnchor
getToolWindowAnchor(final String id
) {
1149 ApplicationManager
.getApplication().assertIsDispatchThread();
1151 return getInfo(id
).getAnchor();
1154 void setToolWindowAnchor(final String id
, final ToolWindowAnchor anchor
) {
1155 ApplicationManager
.getApplication().assertIsDispatchThread();
1156 setToolWindowAnchor(id
, anchor
, -1);
1159 void setToolWindowAnchor(final String id
, final ToolWindowAnchor anchor
, final int order
) {
1160 ApplicationManager
.getApplication().assertIsDispatchThread();
1161 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1162 setToolWindowAnchorImpl(id
, anchor
, order
, commandList
);
1163 execute(commandList
);
1166 private void setToolWindowAnchorImpl(final String id
,
1167 final ToolWindowAnchor anchor
,
1169 final ArrayList
<FinalizableCommand
> commandsList
) {
1171 final WindowInfoImpl info
= getInfo(id
);
1172 if (anchor
== info
.getAnchor() && order
== info
.getOrder()) {
1175 // if tool window isn't visible or only order number is changed then just remove/add stripe button
1176 if (!info
.isVisible() || anchor
== info
.getAnchor() || info
.isFloating()) {
1177 appendRemoveButtonCmd(id
, commandsList
);
1178 myLayout
.setAnchor(id
, anchor
, order
);
1179 // update infos for all window. Actually we have to update only infos affected by
1181 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1182 for (WindowInfoImpl info1
: infos
) {
1183 appendApplyWindowInfoCmd(info1
, commandsList
);
1185 appendAddButtonCmd(getStripeButton(id
), info
, commandsList
);
1187 else { // for docked and sliding windows we have to move buttons and window's decorators
1188 info
.setVisible(false);
1189 appendRemoveDecoratorCmd(id
, false, commandsList
);
1190 appendRemoveButtonCmd(id
, commandsList
);
1191 myLayout
.setAnchor(id
, anchor
, order
);
1192 // update infos for all window. Actually we have to update only infos affected by
1194 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1195 for (WindowInfoImpl info1
: infos
) {
1196 appendApplyWindowInfoCmd(info1
, commandsList
);
1198 appendAddButtonCmd(getStripeButton(id
), info
, commandsList
);
1199 showToolWindowImpl(id
, false, commandsList
);
1200 if (info
.isActive()) {
1201 appendRequestFocusInToolWindowCmd(id
, commandsList
, true);
1206 boolean isSplitMode(String id
) {
1207 ApplicationManager
.getApplication().assertIsDispatchThread();
1209 return getInfo(id
).isSplit();
1212 ToolWindowContentUiType
getContentUiType(String id
) {
1213 ApplicationManager
.getApplication().assertIsDispatchThread();
1215 return getInfo(id
).getContentUiType();
1218 void setSideTool(String id
, boolean isSide
) {
1219 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1220 setSplitModeImpl(id
, isSide
, commandList
);
1221 execute(commandList
);
1224 public void setContentUiType(String id
, ToolWindowContentUiType type
) {
1225 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1227 WindowInfoImpl info
= getInfo(id
);
1228 info
.setContentUiType(type
);
1229 appendApplyWindowInfoCmd(info
, commandList
);
1230 execute(commandList
);
1233 void setSideToolAndAnchor(String id
, ToolWindowAnchor anchor
, int order
, boolean isSide
) {
1234 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1235 setToolWindowAnchor(id
, anchor
, order
);
1236 setSplitModeImpl(id
, isSide
, commandList
);
1237 execute(commandList
);
1240 private void setSplitModeImpl(final String id
, final boolean isSplit
, final ArrayList
<FinalizableCommand
> commandList
) {
1242 final WindowInfoImpl info
= getInfo(id
);
1243 if (isSplit
== info
.isSplit()) {
1247 myLayout
.setSplitMode(id
, isSplit
);
1249 boolean wasActive
= info
.isActive();
1251 deactivateToolWindowImpl(id
, true, commandList
);
1253 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1254 for (WindowInfoImpl info1
: infos
) {
1255 appendApplyWindowInfoCmd(info1
, commandList
);
1258 activateToolWindowImpl(id
, commandList
, true, true);
1260 commandList
.add(myToolWindowsPane
.createUpdateButtonPositionCmd(id
, myWindowManager
.getCommandProcessor()));
1263 ToolWindowType
getToolWindowInternalType(final String id
) {
1264 ApplicationManager
.getApplication().assertIsDispatchThread();
1266 return getInfo(id
).getInternalType();
1269 ToolWindowType
getToolWindowType(final String id
) {
1270 ApplicationManager
.getApplication().assertIsDispatchThread();
1272 return getInfo(id
).getType();
1275 private void fireToolWindowRegistered(final String id
) {
1276 final ToolWindowManagerListener
[] listeners
= myListenerList
.getListeners(ToolWindowManagerListener
.class);
1277 for (ToolWindowManagerListener listener
: listeners
) {
1278 listener
.toolWindowRegistered(id
);
1282 private void fireStateChanged() {
1283 final ToolWindowManagerListener
[] listeners
= myListenerList
.getListeners(ToolWindowManagerListener
.class);
1284 for (ToolWindowManagerListener listener
: listeners
) {
1285 listener
.stateChanged();
1289 boolean isToolWindowActive(final String id
) {
1290 ApplicationManager
.getApplication().assertIsDispatchThread();
1292 return getInfo(id
).isActive();
1295 boolean isToolWindowAutoHide(final String id
) {
1296 ApplicationManager
.getApplication().assertIsDispatchThread();
1298 return getInfo(id
).isAutoHide();
1301 public boolean isToolWindowFloating(final String id
) {
1302 ApplicationManager
.getApplication().assertIsDispatchThread();
1304 return getInfo(id
).isFloating();
1307 boolean isToolWindowVisible(final String id
) {
1308 ApplicationManager
.getApplication().assertIsDispatchThread();
1310 return getInfo(id
).isVisible();
1313 void setToolWindowAutoHide(final String id
, final boolean autoHide
) {
1314 ApplicationManager
.getApplication().assertIsDispatchThread();
1315 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1316 setToolWindowAutoHideImpl(id
, autoHide
, commandList
);
1317 execute(commandList
);
1320 private void setToolWindowAutoHideImpl(final String id
, final boolean autoHide
, final ArrayList
<FinalizableCommand
> commandsList
) {
1322 final WindowInfoImpl info
= getInfo(id
);
1323 if (info
.isAutoHide() == autoHide
) {
1326 info
.setAutoHide(autoHide
);
1327 appendApplyWindowInfoCmd(info
, commandsList
);
1328 if (info
.isVisible()) {
1329 prepareForActivation(id
, commandsList
);
1330 showAndActivate(id
, false, commandsList
, true);
1334 void setToolWindowType(final String id
, final ToolWindowType type
) {
1335 ApplicationManager
.getApplication().assertIsDispatchThread();
1336 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1337 setToolWindowTypeImpl(id
, type
, commandList
);
1338 execute(commandList
);
1341 private void setToolWindowTypeImpl(final String id
, final ToolWindowType type
, final ArrayList
<FinalizableCommand
> commandsList
) {
1343 final WindowInfoImpl info
= getInfo(id
);
1344 if (info
.getType() == type
) {
1347 if (info
.isVisible()) {
1348 final boolean dirtyMode
= info
.isDocked() || info
.isSliding();
1349 info
.setVisible(false);
1350 if (info
.isFloating()) {
1351 appendRemoveFloatingDecoratorCmd(info
, commandsList
);
1353 else { // docked and sliding windows
1354 appendRemoveDecoratorCmd(id
, dirtyMode
, commandsList
);
1357 appendApplyWindowInfoCmd(info
, commandsList
);
1358 prepareForActivation(id
, commandsList
);
1359 showAndActivate(id
, dirtyMode
, commandsList
, true);
1360 appendUpdateToolWindowsPaneCmd(commandsList
);
1364 appendApplyWindowInfoCmd(info
, commandsList
);
1368 private void appendApplyWindowInfoCmd(final WindowInfoImpl info
, final List
<FinalizableCommand
> commandsList
) {
1369 final StripeButton button
= getStripeButton(info
.getId());
1370 final InternalDecorator decorator
= getInternalDecorator(info
.getId());
1371 commandsList
.add(new ApplyWindowInfoCmd(info
, button
, decorator
, myWindowManager
.getCommandProcessor()));
1375 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddDecoratorCmd
1377 private void appendAddDecoratorCmd(final InternalDecorator decorator
,
1378 final WindowInfoImpl info
,
1379 final boolean dirtyMode
,
1380 final List
<FinalizableCommand
> commandsList
) {
1381 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1382 final FinalizableCommand command
= myToolWindowsPane
.createAddDecoratorCmd(decorator
, info
, dirtyMode
, commandProcessor
);
1383 commandsList
.add(command
);
1387 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createRemoveDecoratorCmd
1389 private void appendRemoveDecoratorCmd(final String id
, final boolean dirtyMode
, final List
<FinalizableCommand
> commandsList
) {
1390 final FinalizableCommand command
= myToolWindowsPane
.createRemoveDecoratorCmd(id
, dirtyMode
, myWindowManager
.getCommandProcessor());
1391 commandsList
.add(command
);
1394 private void appendRemoveFloatingDecoratorCmd(final WindowInfoImpl info
, final List
<FinalizableCommand
> commandsList
) {
1395 final RemoveFloatingDecoratorCmd command
= new RemoveFloatingDecoratorCmd(info
);
1396 commandsList
.add(command
);
1400 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddButtonCmd
1402 private void appendAddButtonCmd(final StripeButton button
, final WindowInfoImpl info
, final List
<FinalizableCommand
> commandsList
) {
1403 final Comparator comparator
= myLayout
.comparator(info
.getAnchor());
1404 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1405 final FinalizableCommand command
= myToolWindowsPane
.createAddButtonCmd(button
, info
, comparator
, commandProcessor
);
1406 commandsList
.add(command
);
1410 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddButtonCmd
1412 private void appendRemoveButtonCmd(final String id
, final List
<FinalizableCommand
> commandsList
) {
1413 final FinalizableCommand command
= myToolWindowsPane
.createRemoveButtonCmd(id
, myWindowManager
.getCommandProcessor());
1414 commandsList
.add(command
);
1417 private ActionCallback
appendRequestFocusInEditorComponentCmd(final ArrayList
<FinalizableCommand
> commandList
, boolean forced
) {
1418 if (myProject
.isDisposed()) return new ActionCallback
.Done();
1419 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1420 final RequestFocusInEditorComponentCmd command
=
1421 new RequestFocusInEditorComponentCmd(FileEditorManagerEx
.getInstanceEx(myProject
), getFocusManager(), commandProcessor
, forced
);
1422 commandList
.add(command
);
1423 return command
.getDoneCallback();
1426 private void appendRequestFocusInToolWindowCmd(final String id
, final ArrayList
<FinalizableCommand
> commandList
, boolean forced
) {
1427 final ToolWindowImpl toolWindow
= (ToolWindowImpl
)getToolWindow(id
);
1428 final FocusWatcher focusWatcher
= myId2FocusWatcher
.get(id
);
1429 commandList
.add(new RequestFocusInToolWindowCmd(getFocusManager(), toolWindow
, focusWatcher
, myWindowManager
.getCommandProcessor(), forced
));
1433 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createSetEditorComponentCmd
1435 private void appendSetEditorComponentCmd(final JComponent component
, final List
<FinalizableCommand
> commandsList
) {
1436 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1437 final FinalizableCommand command
= myToolWindowsPane
.createSetEditorComponentCmd(component
, commandProcessor
);
1438 commandsList
.add(command
);
1441 private void appendUpdateToolWindowsPaneCmd(final List
<FinalizableCommand
> commandsList
) {
1442 final JRootPane rootPane
= myFrame
.getRootPane();
1443 final FinalizableCommand command
= new UpdateRootPaneCmd(rootPane
, myWindowManager
.getCommandProcessor());
1444 commandsList
.add(command
);
1448 * @return <code>true</code> if tool window with the specified <code>id</code>
1449 * is floating and has modal showing child dialog. Such windows should not be closed
1450 * when auto-hide windows are gone.
1452 private boolean hasModalChild(final WindowInfoImpl info
) {
1453 if (!info
.isVisible() || !info
.isFloating()) {
1456 final FloatingDecorator decorator
= getFloatingDecorator(info
.getId());
1457 LOG
.assertTrue(decorator
!= null);
1458 return isModalOrHasModalChild(decorator
);
1461 private static boolean isModalOrHasModalChild(final Window window
) {
1462 if (window
instanceof Dialog
) {
1463 final Dialog dialog
= (Dialog
)window
;
1464 if (dialog
.isModal() && dialog
.isShowing()) {
1467 final Window
[] ownedWindows
= dialog
.getOwnedWindows();
1468 for (int i
= ownedWindows
.length
- 1; i
>= 0; i
--) {
1469 if (isModalOrHasModalChild(ownedWindows
[i
])) {
1478 * Helper method. It deactivates all tool windows excepting the tool window
1479 * which should be activated.
1481 private void prepareForActivation(final String id
, final List
<FinalizableCommand
> commandList
) {
1482 final WindowInfoImpl toBeActivatedInfo
= getInfo(id
);
1483 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1484 for (final WindowInfoImpl info
: infos
) {
1485 if (id
.equals(info
.getId())) {
1488 if (toBeActivatedInfo
.isDocked() || toBeActivatedInfo
.isSliding()) {
1489 deactivateToolWindowImpl(info
.getId(), info
.isAutoHide() || info
.isSliding(), commandList
);
1491 else { // floating window is being activated
1492 deactivateToolWindowImpl(info
.getId(), info
.isAutoHide() && info
.isFloating() && !hasModalChild(info
), commandList
);
1497 public void clearSideStack() {
1498 mySideStack
.clear();
1501 public void readExternal(final Element element
) {
1502 for (final Object o
: element
.getChildren()) {
1503 final Element e
= (Element
)o
;
1504 if (EDITOR_ELEMENT
.equals(e
.getName())) {
1505 myEditorComponentActive
= Boolean
.valueOf(e
.getAttributeValue(ACTIVE_ATTR_VALUE
)).booleanValue();
1507 else if (DesktopLayout
.TAG
.equals(e
.getName())) { // read layout of tool windows
1508 myLayout
.readExternal(e
);
1509 final WindowInfoImpl
[] windowInfos
= myLayout
.getAllInfos();
1510 for (WindowInfoImpl windowInfo
: windowInfos
) {
1511 myRestoredToolWindowIds
.add(windowInfo
.getId());
1517 public void writeExternal(final Element element
) {
1518 if (myFrame
== null) {
1519 // do nothing if the project was not opened
1522 final String
[] ids
= getToolWindowIds();
1524 // Update size of all open floating windows. See SCR #18439
1525 for (final String id
: ids
) {
1526 final WindowInfoImpl info
= getInfo(id
);
1527 if (info
.isVisible()) {
1528 final InternalDecorator decorator
= getInternalDecorator(id
);
1529 LOG
.assertTrue(decorator
!= null);
1530 decorator
.fireResized();
1534 // Save frame's bounds
1535 final Rectangle frameBounds
= myFrame
.getBounds();
1536 final Element frameElement
= new Element(FRAME_ELEMENT
);
1537 element
.addContent(frameElement
);
1538 frameElement
.setAttribute(X_ATTR
, Integer
.toString(frameBounds
.x
));
1539 frameElement
.setAttribute(Y_ATTR
, Integer
.toString(frameBounds
.y
));
1540 frameElement
.setAttribute(WIDTH_ATTR
, Integer
.toString(frameBounds
.width
));
1541 frameElement
.setAttribute(HEIGHT_ATTR
, Integer
.toString(frameBounds
.height
));
1542 frameElement
.setAttribute(EXTENDED_STATE_ATTR
, Integer
.toString(myFrame
.getExtendedState()));
1543 // Save whether editor is active or not
1544 final Element editorElement
= new Element(EDITOR_ELEMENT
);
1545 editorElement
.setAttribute(ACTIVE_ATTR_VALUE
, myEditorComponentActive ? Boolean
.TRUE
.toString() : Boolean
.FALSE
.toString());
1546 element
.addContent(editorElement
);
1547 // Save layout of tool windows
1548 final Element layoutElement
= new Element(DesktopLayout
.TAG
);
1549 element
.addContent(layoutElement
);
1550 myLayout
.writeExternal(layoutElement
);
1553 public void setDefaultState(@NotNull final ToolWindowImpl toolWindow
,
1554 @Nullable final ToolWindowAnchor anchor
,
1555 @Nullable final ToolWindowType type
,
1556 @Nullable final Rectangle floatingBounds
) {
1558 final WindowInfoImpl info
= getInfo(toolWindow
.getId());
1559 if (info
.wasRead()) return;
1561 if (floatingBounds
!= null) {
1562 info
.setFloatingBounds(floatingBounds
);
1565 if (anchor
!= null) {
1566 toolWindow
.setAnchor(anchor
, null);
1570 toolWindow
.setType(type
, null);
1575 public void setDefaultContentUiType(ToolWindowImpl toolWindow
, ToolWindowContentUiType type
) {
1576 final WindowInfoImpl info
= getInfo(toolWindow
.getId());
1577 if (info
.wasRead()) return;
1578 toolWindow
.setContentUiType(type
, null);
1582 public void doWhenFocusSettlesDown(@NotNull final Runnable runnable
) {
1583 final boolean needsRestart
= isIdleQueueEmpty();
1584 myIdleRequests
.add(runnable
);
1590 private void restartIdleAlarm() {
1591 myIdleAlarm
.cancelAllRequests();
1592 myIdleAlarm
.addRequest(myIdleRunnable
, Registry
.intValue("actionSystem.focusIdleTimeout"));
1595 private void flushIdleRequests() {
1597 myFlushingIdleRequestsEntryCount
++;
1599 final KeyEvent
[] events
= myToDispatchOnDone
.toArray(new KeyEvent
[myToDispatchOnDone
.size()]);
1600 IdeEventQueue
.getInstance().getKeyEventDispatcher().resetState();
1602 boolean keyWasPressed
= false;
1604 for (KeyEvent each
: events
) {
1605 if (!isFocusTransferReady()) break;
1607 if (!keyWasPressed
) {
1608 if (each
.getID() == KeyEvent
.KEY_PRESSED
) {
1609 keyWasPressed
= true;
1612 myToDispatchOnDone
.remove(each
);
1617 final Component owner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
1618 if (owner
!= null && SwingUtilities
.getWindowAncestor(owner
) != null) {
1619 myToDispatchOnDone
.remove(each
);
1620 IdeEventQueue
.getInstance().dispatchEvent(
1621 new KeyEvent(owner
, each
.getID(), each
.getWhen(), each
.getModifiersEx(), each
.getKeyCode(), each
.getKeyChar(),
1622 each
.getKeyLocation()));
1630 if (isPendingKeyEventsRedispatched()) {
1631 final Runnable
[] all
= myIdleRequests
.toArray(new Runnable
[myIdleRequests
.size()]);
1632 myIdleRequests
.clear();
1633 for (Runnable each
: all
) {
1639 myFlushingIdleRequestsEntryCount
--;
1640 if (!isIdleQueueEmpty()) {
1646 public boolean isFocusTransferReady() {
1647 if (!myFocusRequests
.isEmpty()) return false;
1648 if (myQueue
== null) return true;
1650 return !myQueue
.isSuspendMode() && !myQueue
.hasFocusEventsPending();
1653 private boolean isIdleQueueEmpty() {
1654 return isPendingKeyEventsRedispatched() && myIdleRequests
.isEmpty();
1657 private boolean isPendingKeyEventsRedispatched() {
1658 return myToDispatchOnDone
.isEmpty();
1661 public boolean dispatch(KeyEvent e
) {
1662 if (!Registry
.is("actionSystem.fixLostTyping")) return false;
1664 if (myFlushingIdleRequestsEntryCount
> 0) return false;
1666 if (!isFocusTransferReady() || !isPendingKeyEventsRedispatched()) {
1667 for (FocusCommand each
: myFocusRequests
) {
1668 final KeyEventProcessor processor
= each
.getProcessor();
1669 if (processor
!= null) {
1670 final Boolean result
= processor
.dispatch(e
, myKeyProcessorContext
);
1671 if (result
!= null) {
1672 return result
.booleanValue();
1677 myToDispatchOnDone
.add(e
);
1687 public void suspendKeyProcessingUntil(final ActionCallback done
) {
1688 requestFocus(new FocusCommand(done
) {
1689 public ActionCallback
run() {
1692 }.saveAllocation(), true);
1695 public Expirable
getTimestamp(final boolean trackOnlyForcedCommands
) {
1696 return new Expirable() {
1697 long myOwnStamp
= trackOnlyForcedCommands ? myForcedCmdTimestamp
: myCmdTimestamp
;
1699 public boolean isExpired() {
1700 return myOwnStamp
< (trackOnlyForcedCommands ? myForcedCmdTimestamp
: myCmdTimestamp
);
1705 public void stretchWidth(ToolWindowImpl toolWindow
, int value
) {
1706 if (!toolWindow
.isVisible()) return;
1708 myToolWindowsPane
.stretchWidth(toolWindow
, value
);
1711 public void stretchHeight(ToolWindowImpl toolWindow
, int value
) {
1712 if (!toolWindow
.isVisible()) return;
1714 myToolWindowsPane
.stretchHeight(toolWindow
, value
);
1719 * This command creates and shows <code>FloatingDecorator</code>.
1721 private final class AddFloatingDecoratorCmd
extends FinalizableCommand
{
1722 private final FloatingDecorator myFloatingDecorator
;
1725 * Creates floating decorator for specified floating decorator.
1727 private AddFloatingDecoratorCmd(final InternalDecorator decorator
, final WindowInfoImpl info
) {
1728 super(myWindowManager
.getCommandProcessor());
1729 myFloatingDecorator
= new FloatingDecorator(myFrame
, info
.copy(), decorator
);
1730 myId2FloatingDecorator
.put(info
.getId(), myFloatingDecorator
);
1731 final Rectangle bounds
= info
.getFloatingBounds();
1732 if (bounds
!= null &&
1734 bounds
.height
> 0 &&
1735 myWindowManager
.isInsideScreenBounds(bounds
.x
, bounds
.y
, bounds
.width
)) {
1736 myFloatingDecorator
.setBounds(bounds
);
1738 else { // place new frame at the center of main frame if there are no floating bounds
1739 Dimension size
= decorator
.getSize();
1740 if (size
.width
== 0 || size
.height
== 0) {
1741 size
= decorator
.getPreferredSize();
1743 myFloatingDecorator
.setSize(size
);
1744 myFloatingDecorator
.setLocationRelativeTo(myFrame
);
1750 myFloatingDecorator
.show();
1759 * This command hides and destroys floating decorator for tool window
1760 * with specified <code>ID</code>.
1762 private final class RemoveFloatingDecoratorCmd
extends FinalizableCommand
{
1763 private final FloatingDecorator myFloatingDecorator
;
1765 private RemoveFloatingDecoratorCmd(final WindowInfoImpl info
) {
1766 super(myWindowManager
.getCommandProcessor());
1767 myFloatingDecorator
= getFloatingDecorator(info
.getId());
1768 myId2FloatingDecorator
.remove(info
.getId());
1769 info
.setFloatingBounds(myFloatingDecorator
.getBounds());
1774 if (Patches
.SPECIAL_WINPUT_METHOD_PROCESSING
) {
1775 myFloatingDecorator
.remove(myFloatingDecorator
.getRootPane());
1777 myFloatingDecorator
.dispose();
1785 public Condition
getExpireCondition() {
1786 return Condition
.FALSE
;
1790 private final class EditorComponentFocusWatcher
extends FocusWatcher
{
1791 protected void focusedComponentChanged(final Component component
, final AWTEvent cause
) {
1792 if (myWindowManager
.getCommandProcessor().getCommandCount() > 0 || component
== null) {
1796 // Sometimes focus gained comes when editor is active. For example it can happen when
1797 // user switches between menus or closes some dialog. In that case we just ignore this event,
1798 // i.e. don't initiate deactivation of tool windows and requesting focus in editor.
1799 if (myEditorComponentActive
) {
1804 final KeyboardFocusManager mgr
= KeyboardFocusManager
.getCurrentKeyboardFocusManager();
1805 final Component owner
= mgr
.getFocusOwner();
1807 IdeFocusManager
.getInstance(myProject
).doWhenFocusSettlesDown(new Runnable() {
1809 if (mgr
.getFocusOwner() == owner
) {
1810 activateEditorComponent(false);
1819 * Notifies window manager about focus traversal in tool window
1821 private final class ToolWindowFocusWatcher
extends FocusWatcher
{
1822 private final String myId
;
1825 private ToolWindowFocusWatcher(final ToolWindowImpl toolWindow
) {
1826 myId
= toolWindow
.getId();
1827 install(toolWindow
.getComponent());
1830 protected boolean isFocusedComponentChangeValid(final Component comp
, final AWTEvent cause
) {
1831 return myWindowManager
.getCommandProcessor().getCommandCount() == 0 && comp
!= null;
1834 protected void focusedComponentChanged(final Component component
, final AWTEvent cause
) {
1835 if (myWindowManager
.getCommandProcessor().getCommandCount() > 0 || component
== null) {
1838 final WindowInfoImpl info
= getInfo(myId
);
1839 myFocusedComponentAlaram
.cancelAllRequests();
1841 if (!info
.isActive()) {
1842 myFocusedComponentAlaram
.addRequest(new EdtRunnable() {
1843 public void runEdt() {
1844 if (!myLayout
.isToolWindowRegistered(myId
)) return;
1845 activateToolWindow(myId
, false, false);
1853 * Spies on IdeToolWindow properties and applies them to the window
1856 private final class MyToolWindowPropertyChangeListener
implements PropertyChangeListener
{
1857 public void propertyChange(final PropertyChangeEvent e
) {
1858 final ToolWindowImpl toolWindow
= (ToolWindowImpl
)e
.getSource();
1859 if (ToolWindowEx
.PROP_AVAILABLE
.equals(e
.getPropertyName())) {
1860 final WindowInfoImpl info
= getInfo(toolWindow
.getId());
1861 if (!toolWindow
.isAvailable() && info
.isVisible()) {
1862 hideToolWindow(toolWindow
.getId(), false);
1869 * Translates events from InternalDecorator into ToolWindowManager method invocations.
1871 private final class MyInternalDecoratorListener
implements InternalDecoratorListener
{
1872 public void anchorChanged(final InternalDecorator source
, final ToolWindowAnchor anchor
) {
1873 setToolWindowAnchor(source
.getToolWindow().getId(), anchor
);
1876 public void autoHideChanged(final InternalDecorator source
, final boolean autoHide
) {
1877 setToolWindowAutoHide(source
.getToolWindow().getId(), autoHide
);
1880 public void hidden(final InternalDecorator source
) {
1881 hideToolWindow(source
.getToolWindow().getId(), false);
1884 public void hiddenSide(final InternalDecorator source
) {
1885 hideToolWindow(source
.getToolWindow().getId(), true);
1888 public void contentUiTypeChanges(InternalDecorator source
, ToolWindowContentUiType type
) {
1889 setContentUiType(source
.getToolWindow().getId(), type
);
1893 * Handles event from decorator and modify weight/floating bounds of the
1894 * tool window depending on decoration type.
1896 public void resized(final InternalDecorator source
) {
1897 final WindowInfoImpl info
= getInfo(source
.getToolWindow().getId());
1898 if (info
.isFloating()) {
1899 final Window owner
= SwingUtilities
.getWindowAncestor(source
);
1900 if (owner
!= null) {
1901 info
.setFloatingBounds(owner
.getBounds());
1904 else { // docked and sliding windows
1905 if (ToolWindowAnchor
.TOP
== info
.getAnchor() || ToolWindowAnchor
.BOTTOM
== info
.getAnchor()) {
1906 info
.setWeight((float)source
.getHeight() / (float)myToolWindowsPane
.getMyLayeredPane().getHeight());
1907 float newSideWeight
= (float)source
.getWidth() / (float)myToolWindowsPane
.getMyLayeredPane().getWidth();
1908 if (newSideWeight
< 1.0f
) {
1909 info
.setSideWeight(newSideWeight
);
1913 info
.setWeight((float)source
.getWidth() / (float)myToolWindowsPane
.getMyLayeredPane().getWidth());
1914 float newSideWeight
= (float)source
.getHeight() / (float)myToolWindowsPane
.getMyLayeredPane().getHeight();
1915 if (newSideWeight
< 1.0f
) {
1916 info
.setSideWeight(newSideWeight
);
1922 public void activated(final InternalDecorator source
) {
1923 activateToolWindow(source
.getToolWindow().getId(), true, true);
1926 public void typeChanged(final InternalDecorator source
, final ToolWindowType type
) {
1927 setToolWindowType(source
.getToolWindow().getId(), type
);
1930 public void sideStatusChanged(final InternalDecorator source
, final boolean isSideTool
) {
1931 setSideTool(source
.getToolWindow().getId(), isSideTool
);
1935 private void updateComponentTreeUI() {
1936 ApplicationManager
.getApplication().assertIsDispatchThread();
1937 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1938 for (final WindowInfoImpl info
: infos
) {
1939 if (info
.isVisible()) { // skip visible tool windows (optimization)
1942 SwingUtilities
.updateComponentTreeUI(getInternalDecorator(info
.getId()));
1946 private final class MyUIManagerPropertyChangeListener
implements PropertyChangeListener
{
1947 public void propertyChange(final PropertyChangeEvent e
) {
1948 updateComponentTreeUI();
1952 private final class MyLafManagerListener
implements LafManagerListener
{
1953 public void lookAndFeelChanged(final LafManager source
) {
1954 updateComponentTreeUI();
1959 public WindowManagerEx
getWindowManager() {
1960 return myWindowManager
;
1964 public String
getComponentName() {
1965 return "ToolWindowManager";
1969 public ToolWindowsPane
getToolWindowsPane() {
1970 return myToolWindowsPane
;
1973 public ActionCallback
requestFocus(final Component c
, final boolean forced
) {
1974 return requestFocus(new FocusCommand
.ByComponent(c
), forced
);
1977 public ActionCallback
requestDefaultFocus(final boolean forced
) {
1978 return requestFocus(new FocusCommand() {
1979 public ActionCallback
run() {
1980 return processDefaultFocusRequest(forced
);
1985 private ActionCallback
processDefaultFocusRequest(boolean forced
) {
1986 if (ModalityState
.NON_MODAL
.equals(ModalityState
.current())) {
1987 final String activeId
= getActiveToolWindowId();
1988 if (myEditorComponentActive
|| activeId
== null || getToolWindow(activeId
) == null) {
1989 activateEditorComponent(forced
);
1991 activateToolWindow(activeId
, forced
, false);
1994 return new ActionCallback
.Done();
1997 public ActionCallback
requestFocus(final FocusCommand command
, final boolean forced
) {
1998 final ActionCallback result
= new ActionCallback();
2001 myFocusRequests
.add(command
);
2003 SwingUtilities
.invokeLater(new Runnable() {
2005 resetUnforcedCommand(command
);
2006 _requestFocus(command
, forced
, result
);
2011 _requestFocus(command
, forced
, result
);
2014 result
.doWhenProcessed(new Runnable() {
2023 private void _requestFocus(final FocusCommand command
, final boolean forced
, final ActionCallback result
) {
2024 if (checkForRejectOrByPass(command
, forced
, result
)) return;
2026 setCommand(command
);
2029 myForcedFocusRequestsAlarm
.cancelAllRequests();
2030 setLastEffectiveForcedRequest(command
);
2033 SwingUtilities
.invokeLater(new Runnable() {
2035 if (checkForRejectOrByPass(command
, forced
, result
)) return;
2037 if (myRequestFocusCmd
== command
) {
2038 final ActionCallback
.TimedOut focusTimeout
=
2039 new ActionCallback
.TimedOut(Registry
.intValue("actionSystem.commandProcessingTimeout"),
2040 "Focus command timed out, cmd=" + command
, command
.getAllocation(), true) {
2042 protected void onTimeout() {
2043 forceFinishFocusSettledown(command
, result
);
2049 myForcedCmdTimestamp
++;
2052 command
.run().doWhenDone(new Runnable() {
2054 SwingUtilities
.invokeLater(new Runnable() {
2060 }).doWhenRejected(new Runnable() {
2062 result
.setRejected();
2064 }).doWhenProcessed(new Runnable() {
2066 resetCommand(command
);
2069 myForcedFocusRequestsAlarm
.addRequest(new EdtRunnable() {
2070 public void runEdt() {
2071 setLastEffectiveForcedRequest(null);
2076 }).notify(focusTimeout
);
2079 rejectCommand(command
, result
);
2085 private void forceFinishFocusSettledown(FocusCommand cmd
, ActionCallback cmdCallback
) {
2086 rejectCommand(cmd
, cmdCallback
);
2089 private boolean checkForRejectOrByPass(final FocusCommand cmd
, final boolean forced
, final ActionCallback result
) {
2090 if (cmd
.isExpired()) {
2091 rejectCommand(cmd
, result
);
2095 final FocusCommand lastRequest
= getLastEffectiveForcedRequest();
2097 if (!forced
&& !isUnforcedRequestAllowed()) {
2098 if (cmd
.equals(lastRequest
)) {
2102 rejectCommand(cmd
, result
);
2108 if (lastRequest
!= null && lastRequest
.dominatesOver(cmd
)) {
2109 rejectCommand(cmd
, result
);
2113 boolean doNotExecuteBecauseAppIsInactive
= !myApp
.isActive()
2114 && (!canExecuteOnInactiveApplication(cmd
) && Registry
.is("actionSystem.suspendFocusTransferIfApplicationInactive"));
2115 if (doNotExecuteBecauseAppIsInactive
) {
2116 if (myCallbackOnActivation
!= null) {
2117 myCallbackOnActivation
.setRejected();
2120 myFocusCommandOnAppActivation
= cmd
;
2121 myCallbackOnActivation
= result
;
2129 private void rejectCommand(FocusCommand cmd
, ActionCallback callback
) {
2131 resetUnforcedCommand(cmd
);
2133 callback
.setRejected();
2136 private void setCommand(FocusCommand command
) {
2137 myRequestFocusCmd
= command
;
2139 if (!myFocusRequests
.contains(command
)) {
2140 myFocusRequests
.add(command
);
2144 private void resetCommand(FocusCommand cmd
) {
2145 if (cmd
== myRequestFocusCmd
) {
2146 myRequestFocusCmd
= null;
2149 final KeyEventProcessor processor
= cmd
.getProcessor();
2150 if (processor
!= null) {
2151 processor
.finish(myKeyProcessorContext
);
2154 myFocusRequests
.remove(cmd
);
2157 private void resetUnforcedCommand(FocusCommand cmd
) {
2158 myFocusRequests
.remove(cmd
);
2161 private static boolean canExecuteOnInactiveApplication(FocusCommand cmd
) {
2162 return !Patches
.REQUEST_FOCUS_MAY_ACTIVATE_APP
|| cmd
.canExecuteOnInactiveApp();
2165 private void setLastEffectiveForcedRequest(FocusCommand command
) {
2166 myLastForcedRequest
= new WeakReference
<FocusCommand
>(command
);
2170 private FocusCommand
getLastEffectiveForcedRequest() {
2171 if (myLastForcedRequest
== null) return null;
2172 final FocusCommand request
= myLastForcedRequest
.get();
2173 return request
!= null && !request
.isExpired() ? request
: null;
2176 private boolean isUnforcedRequestAllowed() {
2177 return getLastEffectiveForcedRequest() == null;
2180 private boolean isProjectComponent(Component c
) {
2181 final Component frame
= UIUtil
.findUltimateParent(c
);
2182 if (frame
instanceof IdeFrame
) {
2183 return frame
== myWindowManager
.getFrame(myProject
);
2190 private class AppListener
extends ApplicationAdapter
{
2193 public void applicationDeactivated(IdeFrame ideFrame
) {
2195 final Component owner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
2196 if (isProjectComponent(owner
)) {
2199 c
= getLastFocusedProjectComponent();
2202 myFocusedComponentOnDeactivation
= c
!= null ?
new WeakReference
<Component
>(c
) : null;
2206 public void applicationActivated(final IdeFrame ideFrame
) {
2207 final FocusCommand cmd
= myFocusCommandOnAppActivation
;
2208 ActionCallback callback
= myCallbackOnActivation
;
2209 myFocusCommandOnAppActivation
= null;
2210 myCallbackOnActivation
= null;
2213 requestFocus(cmd
, true).notify(callback
).doWhenRejected(new Runnable() {
2215 focusLastFocusedComponent(ideFrame
);
2219 focusLastFocusedComponent(ideFrame
);
2223 private void focusLastFocusedComponent(IdeFrame ideFrame
) {
2224 final KeyboardFocusManager mgr
= KeyboardFocusManager
.getCurrentKeyboardFocusManager();
2225 if (ideFrame
== myWindowManager
.getFrame(myProject
)) {
2226 final Component owner
= mgr
.getFocusOwner();
2227 Component old
= myFocusedComponentOnDeactivation
!= null ? myFocusedComponentOnDeactivation
.get() : null;
2228 if (owner
== null && old
!= null && old
.isShowing()) {
2229 requestFocus(old
, false);
2231 myFocusedComponentOnDeactivation
= null;
2236 private static class EdtAlarm
{
2237 private final Alarm myAlarm
;
2239 private EdtAlarm(Disposable parent
) {
2240 myAlarm
= new Alarm(Alarm
.ThreadToUse
.OWN_THREAD
, parent
);
2243 public void cancelAllRequests() {
2244 myAlarm
.cancelAllRequests();
2247 public void addRequest(EdtRunnable runnable
, int delay
) {
2248 myAlarm
.addRequest(runnable
, delay
);
2252 private class KeyProcessorConext
implements KeyEventProcessor
.Context
{
2253 public List
<KeyEvent
> getQueue() {
2254 return myToDispatchOnDone
;
2257 public void dispatch(final List
<KeyEvent
> events
) {
2258 doWhenFocusSettlesDown(new Runnable() {
2260 myToDispatchOnDone
.addAll(events
);