1 package com
.intellij
.openapi
.wm
.impl
;
3 import com
.intellij
.Patches
;
4 import com
.intellij
.ide
.IdeEventQueue
;
5 import com
.intellij
.ide
.ui
.LafManager
;
6 import com
.intellij
.ide
.ui
.LafManagerListener
;
7 import com
.intellij
.openapi
.Disposable
;
8 import com
.intellij
.openapi
.application
.Application
;
9 import com
.intellij
.openapi
.application
.ApplicationAdapter
;
10 import com
.intellij
.openapi
.application
.ApplicationManager
;
11 import com
.intellij
.openapi
.application
.ModalityState
;
12 import com
.intellij
.openapi
.components
.ProjectComponent
;
13 import com
.intellij
.openapi
.diagnostic
.Logger
;
14 import com
.intellij
.openapi
.extensions
.Extensions
;
15 import com
.intellij
.openapi
.fileEditor
.ex
.FileEditorManagerEx
;
16 import com
.intellij
.openapi
.project
.DumbAware
;
17 import com
.intellij
.openapi
.project
.DumbAwareRunnable
;
18 import com
.intellij
.openapi
.project
.DumbService
;
19 import com
.intellij
.openapi
.project
.Project
;
20 import com
.intellij
.openapi
.startup
.StartupManager
;
21 import com
.intellij
.openapi
.ui
.MessageType
;
22 import com
.intellij
.openapi
.ui
.popup
.Balloon
;
23 import com
.intellij
.openapi
.ui
.popup
.JBPopupFactory
;
24 import com
.intellij
.openapi
.util
.*;
25 import com
.intellij
.openapi
.util
.registry
.Registry
;
26 import com
.intellij
.openapi
.wm
.*;
27 import com
.intellij
.openapi
.wm
.ex
.*;
28 import com
.intellij
.openapi
.wm
.impl
.commands
.*;
29 import com
.intellij
.ui
.awt
.RelativePoint
;
30 import com
.intellij
.util
.Alarm
;
31 import com
.intellij
.util
.ArrayUtil
;
32 import com
.intellij
.util
.containers
.CollectionFactory
;
33 import com
.intellij
.util
.containers
.HashMap
;
34 import com
.intellij
.util
.containers
.HashSet
;
35 import com
.intellij
.util
.ui
.UIUtil
;
36 import com
.intellij
.util
.ui
.update
.UiNotifyConnector
;
37 import gnu
.trove
.THashSet
;
38 import org
.jdom
.Element
;
39 import org
.jetbrains
.annotations
.NonNls
;
40 import org
.jetbrains
.annotations
.NotNull
;
41 import org
.jetbrains
.annotations
.Nullable
;
44 import javax
.swing
.event
.EventListenerList
;
45 import javax
.swing
.event
.HyperlinkListener
;
47 import java
.awt
.event
.FocusEvent
;
48 import java
.awt
.event
.KeyEvent
;
49 import java
.beans
.PropertyChangeEvent
;
50 import java
.beans
.PropertyChangeListener
;
51 import java
.lang
.ref
.WeakReference
;
53 import java
.util
.List
;
56 * @author Anton Katilin
57 * @author Vladimir Kondratyev
59 public final class ToolWindowManagerImpl
extends ToolWindowManagerEx
implements ProjectComponent
, JDOMExternalizable
{
60 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.wm.impl.ToolWindowManagerImpl");
62 private final Project myProject
;
63 private final WindowManagerEx myWindowManager
;
64 private final EventListenerList myListenerList
;
65 private final DesktopLayout myLayout
;
66 private final HashMap
<String
, InternalDecorator
> myId2InternalDecorator
;
67 private final HashMap
<String
, FloatingDecorator
> myId2FloatingDecorator
;
68 private final HashMap
<String
, StripeButton
> myId2StripeButton
;
69 private final HashMap
<String
, FocusWatcher
> myId2FocusWatcher
;
70 private final Set
<String
> myDumbAwareIds
= Collections
.synchronizedSet(CollectionFactory
.<String
>newTroveSet());
72 private final EditorComponentFocusWatcher myEditorComponentFocusWatcher
;
73 private final MyToolWindowPropertyChangeListener myToolWindowPropertyChangeListener
;
74 private final InternalDecoratorListener myInternalDecoratorListener
;
75 private final MyUIManagerPropertyChangeListener myUIManagerPropertyChangeListener
;
76 private final MyLafManagerListener myLafManagerListener
;
78 private boolean myEditorComponentActive
;
79 private final ActiveStack myActiveStack
;
80 private final SideStack mySideStack
;
82 private ToolWindowsPane myToolWindowsPane
;
83 private IdeFrameImpl myFrame
;
84 private DesktopLayout myLayoutToRestoreLater
= null;
85 @NonNls private static final String EDITOR_ELEMENT
= "editor";
86 @NonNls private static final String ACTIVE_ATTR_VALUE
= "active";
87 @NonNls private static final String FRAME_ELEMENT
= "frame";
88 @NonNls private static final String X_ATTR
= "x";
89 @NonNls private static final String Y_ATTR
= "y";
90 @NonNls private static final String WIDTH_ATTR
= "width";
91 @NonNls private static final String HEIGHT_ATTR
= "height";
92 @NonNls private static final String EXTENDED_STATE_ATTR
= "extended-state";
94 private final EdtAlarm myFocusedComponentAlaram
;
95 private final EdtAlarm myForcedFocusRequestsAlarm
;
97 private final EdtAlarm myIdleAlarm
;
98 private final Set
<Runnable
> myIdleRequests
= new HashSet
<Runnable
>();
99 private final EdtRunnable myIdleRunnable
= new EdtRunnable() {
100 public void runEdt() {
101 if (isFocusTransferReady() && !isIdleQueueEmpty()) {
110 private FocusCommand myRequestFocusCmd
;
111 private ArrayList
<FocusCommand
> myFocusRequests
= new ArrayList
<FocusCommand
>();
113 private FocusCommand myUnforcedRequestFocusCmd
;
115 private ArrayList
<KeyEvent
> myToDispatchOnDone
= new ArrayList
<KeyEvent
>();
116 private int myFlushingIdleRequestsEntryCount
= 0;
118 private WeakReference
<FocusCommand
> myLastForcedRequest
= new WeakReference
<FocusCommand
>(null);
119 private Application myApp
;
120 private AppListener myAppListener
;
122 private FocusCommand myFocusCommandOnAppActivation
;
123 private ActionCallback myCallbackOnActivation
;
124 private WeakReference
<Component
> myFocusedComponentOnDeactivation
;
125 private WeakReference
<Component
> myLastFocusedProjectComponent
;
127 private IdeEventQueue myQueue
;
128 private KeyProcessorConext myKeyProcessorContext
= new KeyProcessorConext();
131 * invoked by reflection
133 public ToolWindowManagerImpl(final Project project
, WindowManagerEx windowManagerEx
, Application app
) {
134 myQueue
= IdeEventQueue
.getInstance();
136 myWindowManager
= windowManagerEx
;
137 myListenerList
= new EventListenerList();
139 myLayout
= new DesktopLayout();
140 myLayout
.copyFrom(windowManagerEx
.getLayout());
142 myId2InternalDecorator
= new HashMap
<String
, InternalDecorator
>();
143 myId2FloatingDecorator
= new HashMap
<String
, FloatingDecorator
>();
144 myId2StripeButton
= new HashMap
<String
, StripeButton
>();
145 myId2FocusWatcher
= new HashMap
<String
, FocusWatcher
>();
147 myEditorComponentFocusWatcher
= new EditorComponentFocusWatcher();
148 myToolWindowPropertyChangeListener
= new MyToolWindowPropertyChangeListener();
149 myInternalDecoratorListener
= new MyInternalDecoratorListener();
150 myUIManagerPropertyChangeListener
= new MyUIManagerPropertyChangeListener();
151 myLafManagerListener
= new MyLafManagerListener();
152 myEditorComponentActive
= false;
153 myActiveStack
= new ActiveStack();
154 mySideStack
= new SideStack();
156 myFocusedComponentAlaram
= new EdtAlarm(project
);
157 myForcedFocusRequestsAlarm
= new EdtAlarm(project
);
158 myIdleAlarm
= new EdtAlarm(project
);
161 myAppListener
= new AppListener();
162 myApp
.addApplicationListener(myAppListener
);
164 IdeEventQueue
.getInstance().addDispatcher(new IdeEventQueue
.EventDispatcher() {
165 public boolean dispatch(AWTEvent e
) {
166 if (e
instanceof FocusEvent
) {
167 final FocusEvent fe
= (FocusEvent
)e
;
168 final Component c
= fe
.getComponent();
169 final IdeFrameImpl frame
= myWindowManager
.getFrame(myProject
);
170 if (c
instanceof Window
|| c
== null || frame
== null) return false;
171 if (isProjectComponent(c
)) {
172 if (fe
.getID() == FocusEvent
.FOCUS_GAINED
) {
173 myLastFocusedProjectComponent
= new WeakReference
<Component
>(c
);
183 private Component
getLastFocusedProjectComponent() {
184 return myLastFocusedProjectComponent
!= null ? myLastFocusedProjectComponent
.get() : null;
187 public Project
getProject() {
191 public void initComponent() {
194 public void disposeComponent() {
195 myApp
.removeApplicationListener(myAppListener
);
198 public void projectOpened() {
199 UIManager
.addPropertyChangeListener(myUIManagerPropertyChangeListener
);
200 LafManager
.getInstance().addLafManagerListener(myLafManagerListener
);
202 myFrame
= myWindowManager
.allocateFrame(myProject
);
203 LOG
.assertTrue(myFrame
!= null);
205 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
207 myToolWindowsPane
= new ToolWindowsPane(myFrame
, this);
208 ((IdeRootPane
)myFrame
.getRootPane()).setToolWindowsPane(myToolWindowsPane
);
209 appendUpdateToolWindowsPaneCmd(commandsList
);
211 myFrame
.setTitle(FrameTitleBuilder
.getInstance().getProjectTitle(myProject
));
213 final JComponent editorComponent
= FileEditorManagerEx
.getInstanceEx(myProject
).getComponent();
214 myEditorComponentFocusWatcher
.install(editorComponent
);
216 appendSetEditorComponentCmd(editorComponent
, commandsList
);
217 if (myEditorComponentActive
) {
218 activateEditorComponentImpl(commandsList
, true);
220 execute(commandsList
);
222 final DumbService
.DumbModeListener dumbModeListener
= new DumbService
.DumbModeListener() {
223 private final Set
<String
> hiddenIds
= new THashSet
<String
>();
225 public void enteredDumbMode() {
228 public void beforeEnteringDumbMode() {
229 for (final String id
: getToolWindowIds()) {
230 if (!myDumbAwareIds
.contains(id
)) {
231 if (isToolWindowVisible(id
)) {
233 hideToolWindow(id
, true);
235 getStripeButton(id
).setEnabled(false);
240 public void exitDumbMode() {
241 for (final String id
: getToolWindowIds()) {
242 getStripeButton(id
).setEnabled(true);
244 for (final String id
: hiddenIds
) {
250 myProject
.getMessageBus().connect().subscribe(DumbService
.DUMB_MODE
, dumbModeListener
);
252 StartupManager
.getInstance(myProject
).registerPostStartupActivity(new DumbAwareRunnable() {
254 registerToolWindowsFromBeans();
255 if (DumbService
.getInstance(myProject
).isDumb()) {
256 dumbModeListener
.beforeEnteringDumbMode();
262 private void registerToolWindowsFromBeans() {
263 ToolWindowEP
[] beans
= Extensions
.getExtensions(ToolWindowEP
.EP_NAME
);
264 for (final ToolWindowEP bean
: beans
) {
265 final Condition condition
= bean
.getCondition();
266 if (condition
!= null && !condition
.value(myProject
)) {
269 ToolWindowAnchor toolWindowAnchor
;
271 toolWindowAnchor
= ToolWindowAnchor
.fromText(bean
.anchor
);
273 catch (Exception e
) {
277 JLabel label
= new JLabel("Initializing toolwindow...");
278 final ToolWindowFactory factory
= bean
.getToolWindowFactory();
279 final ToolWindow toolWindow
= registerToolWindow(bean
.id
, label
, toolWindowAnchor
, myProject
, factory
instanceof DumbAware
);
280 if (bean
.icon
!= null) {
281 Icon icon
= IconLoader
.findIcon(bean
.icon
, factory
.getClass());
284 icon
= IconLoader
.getIcon(bean
.icon
);
285 } catch (Exception ignored
) {}
287 toolWindow
.setIcon(icon
);
289 toolWindow
.setSplitMode(bean
.secondary
, null);
290 UiNotifyConnector
.doWhenFirstShown(label
, new Runnable() {
292 ApplicationManager
.getApplication().invokeLater(new Runnable() {
294 toolWindow
.getContentManager().removeAllContents(false);
295 factory
.createToolWindowContent(myProject
, toolWindow
);
303 public void projectClosed() {
304 UIManager
.removePropertyChangeListener(myUIManagerPropertyChangeListener
);
305 LafManager
.getInstance().removeLafManagerListener(myLafManagerListener
);
306 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
307 final String
[] ids
= getToolWindowIds();
309 // Remove ToolWindowsPane
311 ((IdeRootPane
)myFrame
.getRootPane()).setToolWindowsPane(null);
312 myWindowManager
.releaseFrame(myFrame
);
313 appendUpdateToolWindowsPaneCmd(commandsList
);
315 // Hide all tool windows
317 for (final String id
: ids
) {
318 deactivateToolWindowImpl(id
, true, commandsList
);
321 // Remove editor component
323 final JComponent editorComponent
= FileEditorManagerEx
.getInstanceEx(myProject
).getComponent();
324 myEditorComponentFocusWatcher
.deinstall(editorComponent
);
325 appendSetEditorComponentCmd(null, commandsList
);
326 execute(commandsList
);
329 public void addToolWindowManagerListener(final ToolWindowManagerListener l
) {
330 myListenerList
.add(ToolWindowManagerListener
.class, l
);
333 public void removeToolWindowManagerListener(final ToolWindowManagerListener l
) {
334 myListenerList
.remove(ToolWindowManagerListener
.class, l
);
338 * This is helper method. It delegated its fuctionality to the WindowManager.
339 * Before delegating it fires state changed.
341 private void execute(final ArrayList
<FinalizableCommand
> commandList
) {
343 for (FinalizableCommand each
: commandList
) {
344 each
.beforeExecute(this);
346 myWindowManager
.getCommandProcessor().execute(commandList
, myProject
.getDisposed());
349 public void activateEditorComponent() {
350 activateEditorComponent(true);
353 private void activateEditorComponent(boolean forced
) {
354 if (LOG
.isDebugEnabled()) {
355 LOG
.debug("enter: activateEditorComponent()");
357 ApplicationManager
.getApplication().assertIsDispatchThread();
358 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
359 activateEditorComponentImpl(commandList
, forced
);
360 execute(commandList
);
363 private void activateEditorComponentImpl(final ArrayList
<FinalizableCommand
> commandList
, final boolean forced
) {
364 final String active
= getActiveToolWindowId();
365 // Now we have to request focus into most recent focused editor
366 appendRequestFocusInEditorComponentCmd(commandList
, forced
).doWhenDone(new Runnable() {
368 final ArrayList
<FinalizableCommand
> postExecute
= new ArrayList
<FinalizableCommand
>();
370 if (LOG
.isDebugEnabled()) {
371 LOG
.debug("editor activated");
373 deactivateWindows(postExecute
, null);
374 myActiveStack
.clear();
375 myEditorComponentActive
= true;
377 execute(postExecute
);
379 }).doWhenRejected(new Runnable() {
382 requestFocus(new FocusCommand() {
383 public ActionCallback
run() {
384 final ArrayList
<FinalizableCommand
> cmds
= new ArrayList
<FinalizableCommand
>();
386 final WindowInfoImpl toReactivate
= getInfo(active
);
387 final boolean reactivateLastActive
= toReactivate
!= null && !isToHide(toReactivate
);
388 deactivateWindows(cmds
, reactivateLastActive ? active
: null);
391 if (reactivateLastActive
) {
392 activateToolWindow(active
, false, true);
395 if (active
!= null) {
396 myActiveStack
.remove(active
, false);
399 if (!myActiveStack
.isEmpty()) {
400 activateToolWindow(myActiveStack
.peek(), false, true);
403 return new ActionCallback
.Done();
411 private void deactivateWindows(final ArrayList
<FinalizableCommand
> postExecute
, String idToIgnore
) {
412 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
413 for (final WindowInfoImpl info
: infos
) {
414 final boolean shouldHide
= isToHide(info
);
415 if (idToIgnore
!= null && idToIgnore
.equals(info
.getId())) {
418 deactivateToolWindowImpl(info
.getId(), shouldHide
, postExecute
);
422 private boolean isToHide(final WindowInfoImpl info
) {
423 return (info
.isAutoHide() || info
.isSliding()) && !(info
.isFloating() && hasModalChild(info
));
427 * Helper method. It makes window visible, activates it and request focus into the tool window.
428 * But it doesn't deactivate other tool windows. Use <code>prepareForActivation</code> method to
429 * deactivates other tool windows.
431 * @param dirtyMode if <code>true</code> then all UI operations are performed in "dirty" mode.
432 * It means that UI isn't validated and repainted just after each add/remove operation.
433 * @see ToolWindowManagerImpl#prepareForActivation
435 private void showAndActivate(final String id
,
436 final boolean dirtyMode
,
437 final ArrayList
<FinalizableCommand
> commandsList
,
438 boolean autoFocusContents
) {
439 if (!getToolWindow(id
).isAvailable()) {
443 final WindowInfoImpl info
= getInfo(id
);
444 boolean toApplyInfo
= false;
445 if (!info
.isActive()) {
446 info
.setActive(true);
449 showToolWindowImpl(id
, dirtyMode
, commandsList
);
453 appendApplyWindowInfoCmd(info
, commandsList
);
454 myActiveStack
.push(id
);
455 myEditorComponentActive
= false;
458 if (autoFocusContents
) {
459 appendRequestFocusInToolWindowCmd(id
, commandsList
, true);
463 void activateToolWindow(final String id
, boolean forced
, boolean autoFocusContents
) {
464 if (LOG
.isDebugEnabled()) {
465 LOG
.debug("enter: activateToolWindow(" + id
+ ")");
467 ApplicationManager
.getApplication().assertIsDispatchThread();
469 if (DumbService
.getInstance(myProject
).isDumb() && !myDumbAwareIds
.contains(id
)) {
473 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
474 activateToolWindowImpl(id
, commandList
, forced
, autoFocusContents
);
475 execute(commandList
);
478 private void activateToolWindowImpl(final String id
,
479 final ArrayList
<FinalizableCommand
> commandList
,
481 boolean autoFocusContents
) {
482 if (!isUnforcedRequestAllowed() && !forced
) return;
484 if (LOG
.isDebugEnabled()) {
485 LOG
.debug("enter: activateToolWindowImpl(" + id
+ ")");
487 if (!getToolWindow(id
).isAvailable()) {
488 // Tool window can be "logically" active but not focused. For example,
489 // when the user switched to another application. So we just need to bring
490 // tool window's window to front.
491 final InternalDecorator decorator
= getInternalDecorator(id
);
492 if (!decorator
.hasFocus() && autoFocusContents
) {
493 appendRequestFocusInToolWindowCmd(id
, commandList
, forced
);
497 prepareForActivation(id
, commandList
);
498 showAndActivate(id
, false, commandList
, autoFocusContents
);
502 * Checkes whether the specified <code>id</code> defines installed tool
503 * window. If it's not then throws <code>IllegalStateException</code>.
505 * @throws IllegalStateException if tool window isn't installed.
507 private void checkId(final String id
) {
508 if (!myLayout
.isToolWindowRegistered(id
)) {
509 throw new IllegalStateException("window with id=\"" + id
+ "\" isn't registered");
514 * Helper method. It deactivates (and hides) window with specified <code>id</code>.
516 * @param id <code>id</code> of the tool window to be deactivated.
517 * @param shouldHide if <code>true</code> then also hides specified tool window.
519 private void deactivateToolWindowImpl(final String id
, final boolean shouldHide
, final List
<FinalizableCommand
> commandsList
) {
520 if (LOG
.isDebugEnabled()) {
521 LOG
.debug("enter: deactivateToolWindowImpl(" + id
+ "," + shouldHide
+ ")");
523 final WindowInfoImpl info
= getInfo(id
);
524 if (shouldHide
&& info
.isVisible()) {
525 info
.setVisible(false);
526 if (info
.isFloating()) {
527 appendRemoveFloatingDecoratorCmd(info
, commandsList
);
529 else { // docked and sliding windows
530 appendRemoveDecoratorCmd(id
, false, commandsList
);
533 info
.setActive(false);
534 appendApplyWindowInfoCmd(info
, commandsList
);
537 public String
[] getToolWindowIds() {
538 ApplicationManager
.getApplication().assertIsDispatchThread();
539 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
540 final String
[] ids
= ArrayUtil
.newStringArray(infos
.length
);
541 for (int i
= 0; i
< infos
.length
; i
++) {
542 ids
[i
] = infos
[i
].getId();
547 public String
getActiveToolWindowId() {
548 ApplicationManager
.getApplication().assertIsDispatchThread();
549 return myLayout
.getActiveId();
552 public String
getLastActiveToolWindowId() {
553 return getLastActiveToolWindowId(null);
556 public String
getLastActiveToolWindowId(Condition
<JComponent
> condition
) {
557 ApplicationManager
.getApplication().assertIsDispatchThread();
558 String lastActiveToolWindowId
= null;
559 for (int i
= 0; i
< myActiveStack
.getPersistentSize(); i
++) {
560 final String id
= myActiveStack
.peekPersistent(i
);
561 final ToolWindow toolWindow
= getToolWindow(id
);
562 LOG
.assertTrue(toolWindow
!= null);
563 if (toolWindow
.isAvailable()) {
564 if (condition
== null || condition
.value(toolWindow
.getComponent())) {
565 lastActiveToolWindowId
= id
;
570 return lastActiveToolWindowId
;
574 * @return floating decorator for the tool window with specified <code>ID</code>.
576 private FloatingDecorator
getFloatingDecorator(final String id
) {
577 return myId2FloatingDecorator
.get(id
);
581 * @return internal decorator for the tool window with specified <code>ID</code>.
583 private InternalDecorator
getInternalDecorator(final String id
) {
584 return myId2InternalDecorator
.get(id
);
588 * @return tool button for the window with specified <code>ID</code>.
590 private StripeButton
getStripeButton(final String id
) {
591 return myId2StripeButton
.get(id
);
595 * @return info for the tool window with specified <code>ID</code>.
597 private WindowInfoImpl
getInfo(final String id
) {
598 return myLayout
.getInfo(id
, true);
601 public List
<String
> getIdsOn(final ToolWindowAnchor anchor
) {
602 return myLayout
.getVisibleIdsOn(anchor
, this);
605 public ToolWindow
getToolWindow(final String id
) {
606 ApplicationManager
.getApplication().assertIsDispatchThread();
607 if (!myLayout
.isToolWindowRegistered(id
)) {
610 return getInternalDecorator(id
).getToolWindow();
613 void showToolWindow(final String id
) {
614 if (LOG
.isDebugEnabled()) {
615 LOG
.debug("enter: showToolWindow(" + id
+ ")");
617 ApplicationManager
.getApplication().assertIsDispatchThread();
618 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
619 showToolWindowImpl(id
, false, commandList
);
620 execute(commandList
);
623 public void hideToolWindow(final String id
, final boolean hideSide
) {
624 ApplicationManager
.getApplication().assertIsDispatchThread();
626 final WindowInfoImpl info
= getInfo(id
);
627 if (!info
.isVisible()) return;
628 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
629 final boolean wasActive
= info
.isActive();
631 // hide and deactivate
633 deactivateToolWindowImpl(id
, true, commandList
);
635 if (hideSide
|| info
.isFloating()) {
636 final List
<String
> ids
= myLayout
.getVisibleIdsOn(info
.getAnchor(), this);
637 for (String each
: ids
) {
638 myActiveStack
.remove(each
, true);
642 while (!mySideStack
.isEmpty(info
.getAnchor())) {
643 mySideStack
.pop(info
.getAnchor());
646 final String
[] all
= getToolWindowIds();
647 for (String eachId
: all
) {
648 final WindowInfoImpl eachInfo
= getInfo(eachId
);
649 if (eachInfo
.isVisible() && eachInfo
.getAnchor() == info
.getAnchor()) {
650 deactivateToolWindowImpl(eachId
, true, commandList
);
654 activateEditorComponentImpl(commandList
, true);
658 // first of all we have to find tool window that was located at the same side and
661 WindowInfoImpl info2
= null;
662 while (!mySideStack
.isEmpty(info
.getAnchor())) {
663 final WindowInfoImpl storedInfo
= mySideStack
.pop(info
.getAnchor());
664 final WindowInfoImpl currentInfo
= getInfo(storedInfo
.getId());
665 LOG
.assertTrue(currentInfo
!= null);
666 // SideStack contains copies of real WindowInfos. It means that
667 // these stored infos can be invalid. The following loop removes invalid WindowInfos.
668 if (storedInfo
.getAnchor() == currentInfo
.getAnchor() &&
669 storedInfo
.getType() == currentInfo
.getType() &&
670 storedInfo
.isAutoHide() == currentInfo
.isAutoHide()) {
676 showToolWindowImpl(info2
.getId(), false, commandList
);
679 // If we hide currently active tool window then we should activate the previous
680 // one which is located in the tool window stack.
681 // Activate another tool window if no active tool window exists and
682 // window stack is enabled.
684 myActiveStack
.remove(id
, false); // hidden window should be at the top of stack
686 if (myActiveStack
.isEmpty()) {
687 activateEditorComponentImpl(commandList
, false);
690 final String toBeActivatedId
= myActiveStack
.pop();
691 if (toBeActivatedId
!= null) {
692 activateToolWindowImpl(toBeActivatedId
, commandList
, false, true);
698 execute(commandList
);
702 * @param dirtyMode if <code>true</code> then all UI operations are performed in dirty mode.
704 private void showToolWindowImpl(final String id
, final boolean dirtyMode
, final List
<FinalizableCommand
> commandsList
) {
705 final WindowInfoImpl toBeShownInfo
= getInfo(id
);
706 if (toBeShownInfo
.isVisible() || !getToolWindow(id
).isAvailable()) {
710 if (DumbService
.getInstance(myProject
).isDumb() && !myDumbAwareIds
.contains(id
)) {
714 toBeShownInfo
.setVisible(true);
715 final InternalDecorator decorator
= getInternalDecorator(id
);
717 if (toBeShownInfo
.isFloating()) {
718 commandsList
.add(new AddFloatingDecoratorCmd(decorator
, toBeShownInfo
));
720 else { // docked and sliding windows
722 // If there is tool window on the same side then we have to hide it, i.e.
723 // clear place for tool window to be shown.
725 // We store WindowInfo of hidden tool window in the SideStack (if the tool window
726 // is docked and not auto-hide one). Therefore it's possible to restore the
727 // hidden tool window when showing tool window will be closed.
729 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
730 for (final WindowInfoImpl info
: infos
) {
731 if (id
.equals(info
.getId())) {
734 if (info
.isVisible() &&
735 info
.getType() == toBeShownInfo
.getType() &&
736 info
.getAnchor() == toBeShownInfo
.getAnchor() &&
737 info
.isSplit() == toBeShownInfo
.isSplit()) {
738 // hide and deactivate tool window
739 info
.setVisible(false);
740 appendRemoveDecoratorCmd(info
.getId(), false, commandsList
);
741 if (info
.isActive()) {
742 info
.setActive(false);
744 appendApplyWindowInfoCmd(info
, commandsList
);
745 // store WindowInfo into the SideStack
746 if (info
.isDocked() && !info
.isAutoHide()) {
747 mySideStack
.push(info
);
751 appendAddDecoratorCmd(decorator
, toBeShownInfo
, dirtyMode
, commandsList
);
753 // Remove tool window from the SideStack.
755 mySideStack
.remove(id
);
758 appendApplyWindowInfoCmd(toBeShownInfo
, commandsList
);
761 public ToolWindow
registerToolWindow(@NotNull final String id
,
762 @NotNull final JComponent component
,
763 @NotNull final ToolWindowAnchor anchor
) {
764 return registerToolWindow(id
, component
, anchor
, false);
767 public ToolWindow
registerToolWindow(@NotNull final String id
,
768 @NotNull JComponent component
,
769 @NotNull ToolWindowAnchor anchor
,
770 Disposable parentDisposable
) {
771 return registerToolWindow(id
, component
, anchor
, parentDisposable
, false);
774 public ToolWindow
registerToolWindow(@NotNull final String id
,
775 @NotNull JComponent component
,
776 @NotNull ToolWindowAnchor anchor
,
777 Disposable parentDisposable
,
778 boolean canWorkInDumbMode
) {
779 return registerDisposable(id
, parentDisposable
, registerToolWindow(id
, component
, anchor
, canWorkInDumbMode
));
782 private ToolWindow
registerToolWindow(@NotNull final String id
,
783 @NotNull final JComponent component
,
784 @NotNull final ToolWindowAnchor anchor
,
785 boolean canWorkInDumbMode
) {
786 return registerToolWindow(id
, component
, anchor
, false, false, canWorkInDumbMode
);
789 public ToolWindow
registerToolWindow(@NotNull final String id
, final boolean canCloseContent
, @NotNull final ToolWindowAnchor anchor
) {
790 return registerToolWindow(id
, null, anchor
, false, canCloseContent
, false);
793 public ToolWindow
registerToolWindow(@NotNull final String id
,
794 final boolean canCloseContent
,
795 @NotNull final ToolWindowAnchor anchor
,
796 final boolean sideTool
) {
797 return registerToolWindow(id
, null, anchor
, sideTool
, canCloseContent
, false);
801 public ToolWindow
registerToolWindow(@NotNull final String id
,
802 final boolean canCloseContent
,
803 @NotNull final ToolWindowAnchor anchor
,
804 final Disposable parentDisposable
,
805 final boolean canWorkInDumbMode
) {
806 return registerDisposable(id
, parentDisposable
, registerToolWindow(id
, null, anchor
, false, canCloseContent
, canWorkInDumbMode
));
809 private ToolWindow
registerToolWindow(@NotNull final String id
,
810 @Nullable final JComponent component
,
811 @NotNull final ToolWindowAnchor anchor
,
813 boolean canCloseContent
,
814 final boolean canWorkInDumbMode
) {
815 if (LOG
.isDebugEnabled()) {
816 LOG
.debug("enter: installToolWindow(" + id
+ "," + component
+ "," + anchor
+ "\")");
818 ApplicationManager
.getApplication().assertIsDispatchThread();
819 if (myLayout
.isToolWindowRegistered(id
)) {
820 throw new IllegalArgumentException("window with id=\"" + id
+ "\" is already registered");
823 final WindowInfoImpl info
= myLayout
.register(id
, anchor
, sideTool
);
824 final boolean wasActive
= info
.isActive();
825 final boolean wasVisible
= info
.isVisible();
826 info
.setActive(false);
827 info
.setVisible(false);
831 final ToolWindowImpl toolWindow
= new ToolWindowImpl(this, id
, canCloseContent
, component
);
832 final InternalDecorator decorator
= new InternalDecorator(myProject
, info
.copy(), toolWindow
);
833 myId2InternalDecorator
.put(id
, decorator
);
834 decorator
.addInternalDecoratorListener(myInternalDecoratorListener
);
835 toolWindow
.addPropertyChangeListener(myToolWindowPropertyChangeListener
);
836 myId2FocusWatcher
.put(id
, new ToolWindowFocusWatcher(toolWindow
));
838 // Create and show tool button
840 final StripeButton button
= new StripeButton(decorator
, myToolWindowsPane
);
841 myId2StripeButton
.put(id
, button
);
842 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
843 appendAddButtonCmd(button
, info
, commandsList
);
845 if (canWorkInDumbMode
) {
846 myDumbAwareIds
.add(id
);
847 } else if (DumbService
.getInstance(getProject()).isDumb()) {
848 button
.setEnabled(false);
851 // If preloaded info is visible or active then we have to show/activate the installed
852 // tool window. This step has sense only for windows which are not in the autohide
853 // mode. But if tool window was active but its mode doen't allow to activate it again
854 // (for example, tool window is in autohide mode) then we just activate editor component.
856 if (!info
.isAutoHide() && (info
.isDocked() || info
.isFloating())) {
858 activateToolWindowImpl(info
.getId(), commandsList
, true, true);
860 else if (wasVisible
) {
861 showToolWindowImpl(info
.getId(), false, commandsList
);
864 else if (wasActive
) { // tool window was active but it cannot be activate again
865 activateEditorComponentImpl(commandsList
, true);
868 execute(commandsList
);
869 fireToolWindowRegistered(id
);
873 private ToolWindow
registerDisposable(final String id
, final Disposable parentDisposable
, final ToolWindow window
) {
874 Disposer
.register(parentDisposable
, new Disposable() {
875 public void dispose() {
876 unregisterToolWindow(id
);
882 public void unregisterToolWindow(@NotNull final String id
) {
883 if (LOG
.isDebugEnabled()) {
884 LOG
.debug("enter: unregisterToolWindow(" + id
+ ")");
886 ApplicationManager
.getApplication().assertIsDispatchThread();
887 if (!myLayout
.isToolWindowRegistered(id
)) {
891 final WindowInfoImpl info
= getInfo(id
);
892 final ToolWindowEx toolWindow
= (ToolWindowEx
)getToolWindow(id
);
893 // Save recent appearance of tool window
894 myLayout
.unregister(id
);
895 // Remove decorator and tool button from the screen
896 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
897 if (info
.isVisible()) {
898 info
.setVisible(false);
899 if (info
.isFloating()) {
900 appendRemoveFloatingDecoratorCmd(info
, commandsList
);
902 else { // floating and sliding windows
903 appendRemoveDecoratorCmd(id
, false, commandsList
);
906 appendRemoveButtonCmd(id
, commandsList
);
907 appendApplyWindowInfoCmd(info
, commandsList
);
908 execute(commandsList
);
909 // Remove all references on tool window and save its last properties
910 toolWindow
.removePropertyChangeListener(myToolWindowPropertyChangeListener
);
911 myActiveStack
.remove(id
, true);
912 mySideStack
.remove(id
);
913 // Destroy stripe button
914 final StripeButton button
= getStripeButton(id
);
916 myId2StripeButton
.remove(id
);
918 myId2FocusWatcher
.remove(id
);
920 final InternalDecorator decorator
= getInternalDecorator(id
);
922 decorator
.removeInternalDecoratorListener(myInternalDecoratorListener
);
923 myId2InternalDecorator
.remove(id
);
926 public DesktopLayout
getLayout() {
927 ApplicationManager
.getApplication().assertIsDispatchThread();
931 public void setLayoutToRestoreLater(DesktopLayout layout
) {
932 myLayoutToRestoreLater
= layout
;
935 public DesktopLayout
getLayoutToRestoreLater() {
936 return myLayoutToRestoreLater
;
939 public void setLayout(final DesktopLayout layout
) {
940 ApplicationManager
.getApplication().assertIsDispatchThread();
941 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
942 // hide tool window that are invisible in new layout
943 final WindowInfoImpl
[] currentInfos
= myLayout
.getInfos();
944 for (final WindowInfoImpl currentInfo
: currentInfos
) {
945 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
949 if (currentInfo
.isVisible() && !info
.isVisible()) {
950 deactivateToolWindowImpl(currentInfo
.getId(), true, commandList
);
953 // change anchor of tool windows
954 for (final WindowInfoImpl currentInfo
: currentInfos
) {
955 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
959 if (currentInfo
.getAnchor() != info
.getAnchor() || currentInfo
.getOrder() != info
.getOrder()) {
960 setToolWindowAnchorImpl(currentInfo
.getId(), info
.getAnchor(), info
.getOrder(), commandList
);
963 // change types of tool windows
964 for (final WindowInfoImpl currentInfo
: currentInfos
) {
965 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
969 if (currentInfo
.getType() != info
.getType()) {
970 setToolWindowTypeImpl(currentInfo
.getId(), info
.getType(), commandList
);
973 // change auto-hide state
974 for (final WindowInfoImpl currentInfo
: currentInfos
) {
975 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
979 if (currentInfo
.isAutoHide() != info
.isAutoHide()) {
980 setToolWindowAutoHideImpl(currentInfo
.getId(), info
.isAutoHide(), commandList
);
983 // restore visibility
984 for (final WindowInfoImpl currentInfo
: currentInfos
) {
985 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
989 if (info
.isVisible()) {
990 showToolWindowImpl(currentInfo
.getId(), false, commandList
);
993 // if there is no any active tool window and editor is also inactive
994 // then activate editor
995 if (!myEditorComponentActive
&& getActiveToolWindowId() == null) {
996 activateEditorComponentImpl(commandList
, true);
998 execute(commandList
);
1001 public void invokeLater(final Runnable runnable
) {
1002 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1003 commandList
.add(new InvokeLaterCmd(runnable
, myWindowManager
.getCommandProcessor()));
1004 execute(commandList
);
1007 public IdeFocusManager
getFocusManager() {
1008 return IdeFocusManager
.getInstance(myProject
);
1012 public void notifyByBalloon(@NotNull final String toolWindowId
, @NotNull final MessageType type
, @NotNull final String htmlBody
) {
1013 notifyByBalloon(toolWindowId
, type
, htmlBody
, null, null);
1016 public void notifyByBalloon(@NotNull final String toolWindowId
,
1017 final MessageType type
,
1018 @NotNull final String text
,
1019 @Nullable final Icon icon
,
1020 @Nullable HyperlinkListener listener
) {
1021 checkId(toolWindowId
);
1023 final Stripe stripe
= myToolWindowsPane
.getStripeFor(toolWindowId
);
1024 final ToolWindowImpl window
= getInternalDecorator(toolWindowId
).getToolWindow();
1025 if (!window
.isAvailable()) {
1026 window
.setPlaceholderMode(true);
1027 stripe
.updateState();
1028 stripe
.revalidate();
1032 final ToolWindowAnchor anchor
= getInfo(toolWindowId
).getAnchor();
1033 final Ref
<Balloon
.Position
> position
= Ref
.create(Balloon
.Position
.below
);
1034 if (ToolWindowAnchor
.TOP
== anchor
) {
1035 position
.set(Balloon
.Position
.below
);
1037 else if (ToolWindowAnchor
.BOTTOM
== anchor
) {
1038 position
.set(Balloon
.Position
.above
);
1040 else if (ToolWindowAnchor
.LEFT
== anchor
) {
1041 position
.set(Balloon
.Position
.atRight
);
1043 else if (ToolWindowAnchor
.RIGHT
== anchor
) {
1044 position
.set(Balloon
.Position
.atLeft
);
1047 Icon actualIcon
= icon
!= null ? icon
: type
.getDefaultIcon();
1049 final Balloon balloon
=
1050 JBPopupFactory
.getInstance().createHtmlTextBalloonBuilder(text
.replace("\n", "<br>"), actualIcon
, type
.getPopupBackground(), listener
)
1052 Disposer
.register(balloon
, new Disposable() {
1053 public void dispose() {
1054 window
.setPlaceholderMode(false);
1055 stripe
.updateState();
1056 stripe
.revalidate();
1061 final StripeButton button
= stripe
.getButtonFor(toolWindowId
);
1062 if (button
== null) return;
1064 final Runnable show
= new Runnable() {
1066 if (button
.isShowing()) {
1067 final Point point
= new Point(button
.getBounds().width
/ 2, button
.getHeight() / 2 - 2);
1068 balloon
.show(new RelativePoint(button
, point
), position
.get());
1071 final Rectangle bounds
= myToolWindowsPane
.getBounds();
1072 final Point target
= UIUtil
.getCenterPoint(bounds
, new Dimension(1, 1));
1073 if (ToolWindowAnchor
.TOP
== anchor
) {
1076 else if (ToolWindowAnchor
.BOTTOM
== anchor
) {
1077 target
.y
= bounds
.height
;
1079 else if (ToolWindowAnchor
.LEFT
== anchor
) {
1082 else if (ToolWindowAnchor
.RIGHT
== anchor
) {
1083 target
.x
= bounds
.width
;
1086 balloon
.show(new RelativePoint(myToolWindowsPane
, target
), position
.get());
1091 if (!button
.isValid()) {
1092 SwingUtilities
.invokeLater(new Runnable() {
1104 public boolean isEditorComponentActive() {
1105 ApplicationManager
.getApplication().assertIsDispatchThread();
1106 return myEditorComponentActive
;
1109 ToolWindowAnchor
getToolWindowAnchor(final String id
) {
1110 ApplicationManager
.getApplication().assertIsDispatchThread();
1112 return getInfo(id
).getAnchor();
1115 void setToolWindowAnchor(final String id
, final ToolWindowAnchor anchor
) {
1116 ApplicationManager
.getApplication().assertIsDispatchThread();
1117 setToolWindowAnchor(id
, anchor
, -1);
1120 void setToolWindowAnchor(final String id
, final ToolWindowAnchor anchor
, final int order
) {
1121 ApplicationManager
.getApplication().assertIsDispatchThread();
1122 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1123 setToolWindowAnchorImpl(id
, anchor
, order
, commandList
);
1124 execute(commandList
);
1127 private void setToolWindowAnchorImpl(final String id
,
1128 final ToolWindowAnchor anchor
,
1130 final ArrayList
<FinalizableCommand
> commandsList
) {
1132 final WindowInfoImpl info
= getInfo(id
);
1133 if (anchor
== info
.getAnchor() && order
== info
.getOrder()) {
1136 // if tool window isn't visible or only order number is changed then just remove/add stripe button
1137 if (!info
.isVisible() || anchor
== info
.getAnchor() || info
.isFloating()) {
1138 appendRemoveButtonCmd(id
, commandsList
);
1139 myLayout
.setAnchor(id
, anchor
, order
);
1140 // update infos for all window. Actually we have to update only infos affected by
1142 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1143 for (WindowInfoImpl info1
: infos
) {
1144 appendApplyWindowInfoCmd(info1
, commandsList
);
1146 appendAddButtonCmd(getStripeButton(id
), info
, commandsList
);
1148 else { // for docked and sliding windows we have to move buttons and window's decorators
1149 info
.setVisible(false);
1150 appendRemoveDecoratorCmd(id
, false, commandsList
);
1151 appendRemoveButtonCmd(id
, commandsList
);
1152 myLayout
.setAnchor(id
, anchor
, order
);
1153 // update infos for all window. Actually we have to update only infos affected by
1155 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1156 for (WindowInfoImpl info1
: infos
) {
1157 appendApplyWindowInfoCmd(info1
, commandsList
);
1159 appendAddButtonCmd(getStripeButton(id
), info
, commandsList
);
1160 showToolWindowImpl(id
, false, commandsList
);
1161 if (info
.isActive()) {
1162 appendRequestFocusInToolWindowCmd(id
, commandsList
, true);
1167 boolean isSplitMode(String id
) {
1168 ApplicationManager
.getApplication().assertIsDispatchThread();
1170 return getInfo(id
).isSplit();
1173 void setSideTool(String id
, boolean isSide
) {
1174 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1175 setSplitModeImpl(id
, isSide
, commandList
);
1176 execute(commandList
);
1179 void setSideToolAndAnchor(String id
, ToolWindowAnchor anchor
, int order
, boolean isSide
) {
1180 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1181 setToolWindowAnchor(id
, anchor
, order
);
1182 setSplitModeImpl(id
, isSide
, commandList
);
1183 execute(commandList
);
1186 private void setSplitModeImpl(final String id
, final boolean isSplit
, final ArrayList
<FinalizableCommand
> commandList
) {
1188 final WindowInfoImpl info
= getInfo(id
);
1189 if (isSplit
== info
.isSplit()) {
1193 myLayout
.setSplitMode(id
, isSplit
);
1195 boolean wasActive
= info
.isActive();
1197 deactivateToolWindowImpl(id
, true, commandList
);
1199 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1200 for (WindowInfoImpl info1
: infos
) {
1201 appendApplyWindowInfoCmd(info1
, commandList
);
1204 activateToolWindowImpl(id
, commandList
, true, true);
1206 commandList
.add(myToolWindowsPane
.createUpdateButtonPositionCmd(id
, myWindowManager
.getCommandProcessor()));
1209 ToolWindowType
getToolWindowInternalType(final String id
) {
1210 ApplicationManager
.getApplication().assertIsDispatchThread();
1212 return getInfo(id
).getInternalType();
1215 ToolWindowType
getToolWindowType(final String id
) {
1216 ApplicationManager
.getApplication().assertIsDispatchThread();
1218 return getInfo(id
).getType();
1221 private void fireToolWindowRegistered(final String id
) {
1222 final ToolWindowManagerListener
[] listeners
= myListenerList
.getListeners(ToolWindowManagerListener
.class);
1223 for (ToolWindowManagerListener listener
: listeners
) {
1224 listener
.toolWindowRegistered(id
);
1228 private void fireStateChanged() {
1229 final ToolWindowManagerListener
[] listeners
= myListenerList
.getListeners(ToolWindowManagerListener
.class);
1230 for (ToolWindowManagerListener listener
: listeners
) {
1231 listener
.stateChanged();
1235 boolean isToolWindowActive(final String id
) {
1236 ApplicationManager
.getApplication().assertIsDispatchThread();
1238 return getInfo(id
).isActive();
1241 boolean isToolWindowAutoHide(final String id
) {
1242 ApplicationManager
.getApplication().assertIsDispatchThread();
1244 return getInfo(id
).isAutoHide();
1247 public boolean isToolWindowFloating(final String id
) {
1248 ApplicationManager
.getApplication().assertIsDispatchThread();
1250 return getInfo(id
).isFloating();
1253 boolean isToolWindowVisible(final String id
) {
1254 ApplicationManager
.getApplication().assertIsDispatchThread();
1256 return getInfo(id
).isVisible();
1259 void setToolWindowAutoHide(final String id
, final boolean autoHide
) {
1260 ApplicationManager
.getApplication().assertIsDispatchThread();
1261 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1262 setToolWindowAutoHideImpl(id
, autoHide
, commandList
);
1263 execute(commandList
);
1266 private void setToolWindowAutoHideImpl(final String id
, final boolean autoHide
, final ArrayList
<FinalizableCommand
> commandsList
) {
1268 final WindowInfoImpl info
= getInfo(id
);
1269 if (info
.isAutoHide() == autoHide
) {
1272 info
.setAutoHide(autoHide
);
1273 appendApplyWindowInfoCmd(info
, commandsList
);
1274 if (info
.isVisible()) {
1275 prepareForActivation(id
, commandsList
);
1276 showAndActivate(id
, false, commandsList
, true);
1280 void setToolWindowType(final String id
, final ToolWindowType type
) {
1281 ApplicationManager
.getApplication().assertIsDispatchThread();
1282 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1283 setToolWindowTypeImpl(id
, type
, commandList
);
1284 execute(commandList
);
1287 private void setToolWindowTypeImpl(final String id
, final ToolWindowType type
, final ArrayList
<FinalizableCommand
> commandsList
) {
1289 final WindowInfoImpl info
= getInfo(id
);
1290 if (info
.getType() == type
) {
1293 if (info
.isVisible()) {
1294 final boolean dirtyMode
= info
.isDocked() || info
.isSliding();
1295 info
.setVisible(false);
1296 if (info
.isFloating()) {
1297 appendRemoveFloatingDecoratorCmd(info
, commandsList
);
1299 else { // docked and sliding windows
1300 appendRemoveDecoratorCmd(id
, dirtyMode
, commandsList
);
1303 appendApplyWindowInfoCmd(info
, commandsList
);
1304 prepareForActivation(id
, commandsList
);
1305 showAndActivate(id
, dirtyMode
, commandsList
, true);
1306 appendUpdateToolWindowsPaneCmd(commandsList
);
1310 appendApplyWindowInfoCmd(info
, commandsList
);
1314 private void appendApplyWindowInfoCmd(final WindowInfoImpl info
, final List
<FinalizableCommand
> commandsList
) {
1315 final StripeButton button
= getStripeButton(info
.getId());
1316 final InternalDecorator decorator
= getInternalDecorator(info
.getId());
1317 commandsList
.add(new ApplyWindowInfoCmd(info
, button
, decorator
, myWindowManager
.getCommandProcessor()));
1321 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddDecoratorCmd
1323 private void appendAddDecoratorCmd(final InternalDecorator decorator
,
1324 final WindowInfoImpl info
,
1325 final boolean dirtyMode
,
1326 final List
<FinalizableCommand
> commandsList
) {
1327 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1328 final FinalizableCommand command
= myToolWindowsPane
.createAddDecoratorCmd(decorator
, info
, dirtyMode
, commandProcessor
);
1329 commandsList
.add(command
);
1333 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createRemoveDecoratorCmd
1335 private void appendRemoveDecoratorCmd(final String id
, final boolean dirtyMode
, final List
<FinalizableCommand
> commandsList
) {
1336 final FinalizableCommand command
= myToolWindowsPane
.createRemoveDecoratorCmd(id
, dirtyMode
, myWindowManager
.getCommandProcessor());
1337 commandsList
.add(command
);
1340 private void appendRemoveFloatingDecoratorCmd(final WindowInfoImpl info
, final List
<FinalizableCommand
> commandsList
) {
1341 final RemoveFloatingDecoratorCmd command
= new RemoveFloatingDecoratorCmd(info
);
1342 commandsList
.add(command
);
1346 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddButtonCmd
1348 private void appendAddButtonCmd(final StripeButton button
, final WindowInfoImpl info
, final List
<FinalizableCommand
> commandsList
) {
1349 final Comparator comparator
= myLayout
.comparator(info
.getAnchor());
1350 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1351 final FinalizableCommand command
= myToolWindowsPane
.createAddButtonCmd(button
, info
, comparator
, commandProcessor
);
1352 commandsList
.add(command
);
1356 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddButtonCmd
1358 private void appendRemoveButtonCmd(final String id
, final List
<FinalizableCommand
> commandsList
) {
1359 final FinalizableCommand command
= myToolWindowsPane
.createRemoveButtonCmd(id
, myWindowManager
.getCommandProcessor());
1360 commandsList
.add(command
);
1363 private ActionCallback
appendRequestFocusInEditorComponentCmd(final ArrayList
<FinalizableCommand
> commandList
, boolean forced
) {
1364 if (myProject
.isDisposed()) return new ActionCallback
.Done();
1365 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1366 final RequestFocusInEditorComponentCmd command
=
1367 new RequestFocusInEditorComponentCmd(FileEditorManagerEx
.getInstanceEx(myProject
), commandProcessor
, forced
);
1368 commandList
.add(command
);
1369 return command
.getDoneCallback();
1372 private void appendRequestFocusInToolWindowCmd(final String id
, final ArrayList
<FinalizableCommand
> commandList
, boolean forced
) {
1373 final ToolWindowImpl toolWindow
= (ToolWindowImpl
)getToolWindow(id
);
1374 final FocusWatcher focusWatcher
= myId2FocusWatcher
.get(id
);
1375 commandList
.add(new RequestFocusInToolWindowCmd(toolWindow
, focusWatcher
, myWindowManager
.getCommandProcessor(), forced
));
1379 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createSetEditorComponentCmd
1381 private void appendSetEditorComponentCmd(final JComponent component
, final List
<FinalizableCommand
> commandsList
) {
1382 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1383 final FinalizableCommand command
= myToolWindowsPane
.createSetEditorComponentCmd(component
, commandProcessor
);
1384 commandsList
.add(command
);
1387 private void appendUpdateToolWindowsPaneCmd(final List
<FinalizableCommand
> commandsList
) {
1388 final JRootPane rootPane
= myFrame
.getRootPane();
1389 final FinalizableCommand command
= new UpdateRootPaneCmd(rootPane
, myWindowManager
.getCommandProcessor());
1390 commandsList
.add(command
);
1394 * @return <code>true</code> if tool window with the specified <code>id</code>
1395 * is floating and has modal showing child dialog. Such windows should not be closed
1396 * when auto-hide windows are gone.
1398 private boolean hasModalChild(final WindowInfoImpl info
) {
1399 if (!info
.isVisible() || !info
.isFloating()) {
1402 final FloatingDecorator decorator
= getFloatingDecorator(info
.getId());
1403 LOG
.assertTrue(decorator
!= null);
1404 return isModalOrHasModalChild(decorator
);
1407 private static boolean isModalOrHasModalChild(final Window window
) {
1408 if (window
instanceof Dialog
) {
1409 final Dialog dialog
= (Dialog
)window
;
1410 if (dialog
.isModal() && dialog
.isShowing()) {
1413 final Window
[] ownedWindows
= dialog
.getOwnedWindows();
1414 for (int i
= ownedWindows
.length
- 1; i
>= 0; i
--) {
1415 if (isModalOrHasModalChild(ownedWindows
[i
])) {
1424 * Helper method. It deactivates all tool windows excepting the tool window
1425 * which should be activated.
1427 private void prepareForActivation(final String id
, final List
<FinalizableCommand
> commandList
) {
1428 final WindowInfoImpl toBeActivatedInfo
= getInfo(id
);
1429 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1430 for (final WindowInfoImpl info
: infos
) {
1431 if (id
.equals(info
.getId())) {
1434 if (toBeActivatedInfo
.isDocked() || toBeActivatedInfo
.isSliding()) {
1435 deactivateToolWindowImpl(info
.getId(), info
.isAutoHide() || info
.isSliding(), commandList
);
1437 else { // floating window is being activated
1438 deactivateToolWindowImpl(info
.getId(), info
.isAutoHide() && info
.isFloating() && !hasModalChild(info
), commandList
);
1443 public void clearSideStack() {
1444 mySideStack
.clear();
1447 public void readExternal(final Element element
) {
1448 for (final Object o
: element
.getChildren()) {
1449 final Element e
= (Element
)o
;
1450 if (EDITOR_ELEMENT
.equals(e
.getName())) {
1451 myEditorComponentActive
= Boolean
.valueOf(e
.getAttributeValue(ACTIVE_ATTR_VALUE
)).booleanValue();
1453 else if (DesktopLayout
.TAG
.equals(e
.getName())) { // read layout of tool windows
1454 myLayout
.readExternal(e
);
1459 public void writeExternal(final Element element
) {
1460 if (myFrame
== null) {
1461 // do nothing if the project was not opened
1464 final String
[] ids
= getToolWindowIds();
1466 // Update size of all open floating windows. See SCR #18439
1467 for (final String id
: ids
) {
1468 final WindowInfoImpl info
= getInfo(id
);
1469 if (info
.isVisible()) {
1470 final InternalDecorator decorator
= getInternalDecorator(id
);
1471 LOG
.assertTrue(decorator
!= null);
1472 decorator
.fireResized();
1476 // Save frame's bounds
1477 final Rectangle frameBounds
= myFrame
.getBounds();
1478 final Element frameElement
= new Element(FRAME_ELEMENT
);
1479 element
.addContent(frameElement
);
1480 frameElement
.setAttribute(X_ATTR
, Integer
.toString(frameBounds
.x
));
1481 frameElement
.setAttribute(Y_ATTR
, Integer
.toString(frameBounds
.y
));
1482 frameElement
.setAttribute(WIDTH_ATTR
, Integer
.toString(frameBounds
.width
));
1483 frameElement
.setAttribute(HEIGHT_ATTR
, Integer
.toString(frameBounds
.height
));
1484 frameElement
.setAttribute(EXTENDED_STATE_ATTR
, Integer
.toString(myFrame
.getExtendedState()));
1485 // Save whether editor is active or not
1486 final Element editorElement
= new Element(EDITOR_ELEMENT
);
1487 editorElement
.setAttribute(ACTIVE_ATTR_VALUE
, myEditorComponentActive ? Boolean
.TRUE
.toString() : Boolean
.FALSE
.toString());
1488 element
.addContent(editorElement
);
1489 // Save layout of tool windows
1490 final Element layoutElement
= new Element(DesktopLayout
.TAG
);
1491 element
.addContent(layoutElement
);
1492 myLayout
.writeExternal(layoutElement
);
1495 public void setDefaultState(@NotNull final ToolWindowImpl toolWindow
,
1496 @Nullable final ToolWindowAnchor anchor
,
1497 @Nullable final ToolWindowType type
,
1498 @Nullable final Rectangle floatingBounds
) {
1500 final WindowInfoImpl info
= getInfo(toolWindow
.getId());
1501 if (info
.wasRead()) return;
1503 if (floatingBounds
!= null) {
1504 info
.setFloatingBounds(floatingBounds
);
1507 if (anchor
!= null) {
1508 toolWindow
.setAnchor(anchor
, null);
1512 toolWindow
.setType(type
, null);
1517 public void doWhenFocusSettlesDown(@NotNull final Runnable runnable
) {
1518 final boolean needsRestart
= isIdleQueueEmpty();
1519 myIdleRequests
.add(runnable
);
1525 private void restartIdleAlarm() {
1526 myIdleAlarm
.cancelAllRequests();
1527 myIdleAlarm
.addRequest(myIdleRunnable
, 20);
1530 private void flushIdleRequests() {
1532 myFlushingIdleRequestsEntryCount
++;
1534 final KeyEvent
[] events
= myToDispatchOnDone
.toArray(new KeyEvent
[myToDispatchOnDone
.size()]);
1535 IdeEventQueue
.getInstance().getKeyEventDispatcher().resetState();
1537 for (int i
= 0; i
< events
.length
; i
++) {
1538 KeyEvent each
= events
[i
];
1539 if (!isFocusTransferReady()) break;
1541 final Component owner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
1542 if (owner
!= null && SwingUtilities
.getWindowAncestor(owner
) != null) {
1543 myToDispatchOnDone
.remove(each
);
1544 IdeEventQueue
.getInstance().dispatchEvent(
1545 new KeyEvent(owner
, each
.getID(), each
.getWhen(), each
.getModifiersEx(), each
.getKeyCode(), each
.getKeyChar(),
1546 each
.getKeyLocation()));
1554 if (isPendingKeyEventsRedispatched()) {
1555 final Runnable
[] all
= myIdleRequests
.toArray(new Runnable
[myIdleRequests
.size()]);
1556 myIdleRequests
.clear();
1557 for (Runnable each
: all
) {
1563 myFlushingIdleRequestsEntryCount
--;
1564 if (!isIdleQueueEmpty()) {
1570 private String
toString(KeyEvent e
) {
1571 return KeyStroke
.getKeyStrokeForEvent(e
).toString();
1574 public boolean isFocusTransferReady() {
1575 return myFocusRequests
.isEmpty() && (myQueue
== null || !myQueue
.isSuspendMode());
1578 private boolean isIdleQueueEmpty() {
1579 return isPendingKeyEventsRedispatched() && myIdleRequests
.size() == 0;
1582 private boolean isPendingKeyEventsRedispatched() {
1583 return myToDispatchOnDone
.size() == 0;
1586 public boolean dispatch(KeyEvent e
) {
1587 if (!Registry
.is("actionSystem.fixLostTyping")) return false;
1589 if (myFlushingIdleRequestsEntryCount
> 0) return false;
1591 if (!isFocusTransferReady() || !isPendingKeyEventsRedispatched()) {
1592 for (FocusCommand each
: myFocusRequests
) {
1593 final KeyEventProcessor processor
= each
.getProcessor();
1594 if (processor
!= null) {
1595 final Boolean result
= processor
.dispatch(e
, myKeyProcessorContext
);
1596 if (result
!= null) {
1597 return result
.booleanValue();
1602 myToDispatchOnDone
.add(e
);
1612 public void suspendKeyProcessingUntil(final ActionCallback done
) {
1613 requestFocus(new FocusCommand(done
) {
1614 public ActionCallback
run() {
1617 }.saveAllocation(), true);
1622 * This command creates and shows <code>FloatingDecorator</code>.
1624 private final class AddFloatingDecoratorCmd
extends FinalizableCommand
{
1625 private final FloatingDecorator myFloatingDecorator
;
1628 * Creates floating decorator for specified floating decorator.
1630 public AddFloatingDecoratorCmd(final InternalDecorator decorator
, final WindowInfoImpl info
) {
1631 super(myWindowManager
.getCommandProcessor());
1632 myFloatingDecorator
= new FloatingDecorator(myFrame
, info
.copy(), decorator
);
1633 myId2FloatingDecorator
.put(info
.getId(), myFloatingDecorator
);
1634 final Rectangle bounds
= info
.getFloatingBounds();
1635 if (bounds
!= null &&
1637 bounds
.height
> 0 &&
1638 myWindowManager
.isInsideScreenBounds(bounds
.x
, bounds
.y
, bounds
.width
)) {
1639 myFloatingDecorator
.setBounds(bounds
);
1641 else { // place new frame at the center of main frame if there are no floating bounds
1642 Dimension size
= decorator
.getSize();
1643 if (size
.width
== 0 || size
.height
== 0) {
1644 size
= decorator
.getPreferredSize();
1646 myFloatingDecorator
.setSize(size
);
1647 myFloatingDecorator
.setLocationRelativeTo(myFrame
);
1653 myFloatingDecorator
.show();
1662 * This command hides and destroys floating decorator for tool window
1663 * with specified <code>ID</code>.
1665 private final class RemoveFloatingDecoratorCmd
extends FinalizableCommand
{
1666 private final FloatingDecorator myFloatingDecorator
;
1668 public RemoveFloatingDecoratorCmd(final WindowInfoImpl info
) {
1669 super(myWindowManager
.getCommandProcessor());
1670 myFloatingDecorator
= getFloatingDecorator(info
.getId());
1671 myId2FloatingDecorator
.remove(info
.getId());
1672 info
.setFloatingBounds(myFloatingDecorator
.getBounds());
1677 if (Patches
.SPECIAL_WINPUT_METHOD_PROCESSING
) {
1678 myFloatingDecorator
.remove(myFloatingDecorator
.getRootPane());
1680 myFloatingDecorator
.dispose();
1688 public Condition
getExpireCondition() {
1689 return Condition
.FALSE
;
1693 private final class EditorComponentFocusWatcher
extends FocusWatcher
{
1694 protected void focusedComponentChanged(final Component component
, final AWTEvent cause
) {
1695 if (myWindowManager
.getCommandProcessor().getCommandCount() > 0 || component
== null) {
1699 // Sometimes focus gained comes when editor is active. For example it can happen when
1700 // user switches between menus or closes some dialog. In that case we just ignore this event,
1701 // i.e. don't initiate deactivation of tool windows and requesting focus in editor.
1702 if (myEditorComponentActive
) {
1707 final KeyboardFocusManager mgr
= KeyboardFocusManager
.getCurrentKeyboardFocusManager();
1708 final Component owner
= mgr
.getFocusOwner();
1710 IdeFocusManager
.getInstance(myProject
).doWhenFocusSettlesDown(new Runnable() {
1712 if (mgr
.getFocusOwner() == owner
) {
1713 activateEditorComponent(false);
1722 * Notifies window manager about focus traversal in tool window
1724 private final class ToolWindowFocusWatcher
extends FocusWatcher
{
1725 private final String myId
;
1728 public ToolWindowFocusWatcher(final ToolWindowImpl toolWindow
) {
1729 myId
= toolWindow
.getId();
1730 install(toolWindow
.getComponent());
1733 protected boolean isFocusedComponentChangeValid(final Component comp
, final AWTEvent cause
) {
1734 return myWindowManager
.getCommandProcessor().getCommandCount() == 0 && comp
!= null;
1737 protected void focusedComponentChanged(final Component component
, final AWTEvent cause
) {
1738 if (myWindowManager
.getCommandProcessor().getCommandCount() > 0 || component
== null) {
1741 final WindowInfoImpl info
= getInfo(myId
);
1742 myFocusedComponentAlaram
.cancelAllRequests();
1744 if (!info
.isActive()) {
1745 myFocusedComponentAlaram
.addRequest(new EdtRunnable() {
1746 public void runEdt() {
1747 if (!myLayout
.isToolWindowRegistered(myId
)) return;
1748 activateToolWindow(myId
, false, false);
1756 * Spies on IdeToolWindow properties and applies them to the window
1759 private final class MyToolWindowPropertyChangeListener
implements PropertyChangeListener
{
1760 public void propertyChange(final PropertyChangeEvent e
) {
1761 final ToolWindowImpl toolWindow
= (ToolWindowImpl
)e
.getSource();
1762 if (ToolWindowEx
.PROP_AVAILABLE
.equals(e
.getPropertyName())) {
1763 final WindowInfoImpl info
= getInfo(toolWindow
.getId());
1764 if (!toolWindow
.isAvailable() && info
.isVisible()) {
1765 hideToolWindow(toolWindow
.getId(), false);
1772 * Translates events from InternalDecorator into ToolWindowManager method invocations.
1774 private final class MyInternalDecoratorListener
implements InternalDecoratorListener
{
1775 public void anchorChanged(final InternalDecorator source
, final ToolWindowAnchor anchor
) {
1776 setToolWindowAnchor(source
.getToolWindow().getId(), anchor
);
1779 public void autoHideChanged(final InternalDecorator source
, final boolean autoHide
) {
1780 setToolWindowAutoHide(source
.getToolWindow().getId(), autoHide
);
1783 public void hidden(final InternalDecorator source
) {
1784 hideToolWindow(source
.getToolWindow().getId(), false);
1787 public void hiddenSide(final InternalDecorator source
) {
1788 hideToolWindow(source
.getToolWindow().getId(), true);
1792 * Handles event from decorator and modify weight/floating bounds of the
1793 * tool window depending on decoration type.
1795 public void resized(final InternalDecorator source
) {
1796 final WindowInfoImpl info
= getInfo(source
.getToolWindow().getId());
1797 if (info
.isFloating()) {
1798 final Window owner
= SwingUtilities
.getWindowAncestor(source
);
1799 if (owner
!= null) {
1800 info
.setFloatingBounds(owner
.getBounds());
1803 else { // docked and sliding windows
1804 if (ToolWindowAnchor
.TOP
== info
.getAnchor() || ToolWindowAnchor
.BOTTOM
== info
.getAnchor()) {
1805 info
.setWeight((float)source
.getHeight() / (float)myToolWindowsPane
.getMyLayeredPane().getHeight());
1806 float newSideWeight
= (float)source
.getWidth() / (float)myToolWindowsPane
.getMyLayeredPane().getWidth();
1807 if (newSideWeight
< 1.0f
) {
1808 info
.setSideWeight(newSideWeight
);
1812 info
.setWeight((float)source
.getWidth() / (float)myToolWindowsPane
.getMyLayeredPane().getWidth());
1813 float newSideWeight
= (float)source
.getHeight() / (float)myToolWindowsPane
.getMyLayeredPane().getHeight();
1814 if (newSideWeight
< 1.0f
) {
1815 info
.setSideWeight(newSideWeight
);
1821 public void activated(final InternalDecorator source
) {
1822 activateToolWindow(source
.getToolWindow().getId(), true, true);
1825 public void typeChanged(final InternalDecorator source
, final ToolWindowType type
) {
1826 setToolWindowType(source
.getToolWindow().getId(), type
);
1829 public void sideStatusChanged(final InternalDecorator source
, final boolean isSideTool
) {
1830 setSideTool(source
.getToolWindow().getId(), isSideTool
);
1834 private void updateComponentTreeUI() {
1835 ApplicationManager
.getApplication().assertIsDispatchThread();
1836 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1837 for (final WindowInfoImpl info
: infos
) {
1838 if (info
.isVisible()) { // skip visible tool windows (optimization)
1841 SwingUtilities
.updateComponentTreeUI(getInternalDecorator(info
.getId()));
1845 private final class MyUIManagerPropertyChangeListener
implements PropertyChangeListener
{
1846 public void propertyChange(final PropertyChangeEvent e
) {
1847 updateComponentTreeUI();
1851 private final class MyLafManagerListener
implements LafManagerListener
{
1852 public void lookAndFeelChanged(final LafManager source
) {
1853 updateComponentTreeUI();
1858 public WindowManagerEx
getWindowManager() {
1859 return myWindowManager
;
1863 public String
getComponentName() {
1864 return "ToolWindowManager";
1868 public ToolWindowsPane
getToolWindowsPane() {
1869 return myToolWindowsPane
;
1872 public ActionCallback
requestFocus(final Component c
, final boolean forced
) {
1873 return requestFocus(new FocusCommand
.ByComponent(c
), forced
);
1876 public ActionCallback
requestDefaultFocus(final boolean forced
) {
1877 return requestFocus(new FocusCommand() {
1878 public ActionCallback
run() {
1879 return processDefaultFocusRequest(forced
);
1884 private ActionCallback
processDefaultFocusRequest(boolean forced
) {
1885 if (ModalityState
.NON_MODAL
.equals(ModalityState
.current())) {
1886 if (myEditorComponentActive
) {
1887 activateEditorComponent(forced
);
1889 activateToolWindow(getActiveToolWindowId(), forced
, false);
1892 return new ActionCallback
.Done();
1895 public ActionCallback
requestFocus(final FocusCommand command
, final boolean forced
) {
1896 final ActionCallback result
= new ActionCallback();
1899 myUnforcedRequestFocusCmd
= command
;
1900 myFocusRequests
.add(command
);
1902 SwingUtilities
.invokeLater(new Runnable() {
1904 resetUnforcedCommand(command
);
1905 _requestFocus(command
, forced
, result
);
1910 _requestFocus(command
, forced
, result
);
1913 result
.doWhenProcessed(new Runnable() {
1922 private void _requestFocus(final FocusCommand command
, final boolean forced
, final ActionCallback result
) {
1923 if (checkForRejectOrByPass(command
, forced
, result
)) return;
1925 setCommand(command
);
1928 myForcedFocusRequestsAlarm
.cancelAllRequests();
1929 setLastEffectiveForcedRequest(command
);
1932 SwingUtilities
.invokeLater(new Runnable() {
1934 if (checkForRejectOrByPass(command
, forced
, result
)) return;
1936 if (myRequestFocusCmd
== command
) {
1937 final ActionCallback
.TimedOut focusTimeout
=
1938 new ActionCallback
.TimedOut(Registry
.intValue("actionSystem.commandProcessingTimeout"),
1939 "Focus command timed out, cmd=" + command
, command
.getAllocation(), true) {
1941 protected void onTimeout() {
1942 forceFinishFocusSettledown(command
, result
);
1946 command
.run().doWhenDone(new Runnable() {
1948 SwingUtilities
.invokeLater(new Runnable() {
1954 }).doWhenRejected(new Runnable() {
1956 result
.setRejected();
1958 }).doWhenProcessed(new Runnable() {
1960 resetCommand(command
);
1963 myForcedFocusRequestsAlarm
.addRequest(new EdtRunnable() {
1964 public void runEdt() {
1965 setLastEffectiveForcedRequest(null);
1970 }).notify(focusTimeout
);
1973 rejectCommand(command
, result
);
1979 private void forceFinishFocusSettledown(FocusCommand cmd
, ActionCallback cmdCallback
) {
1980 rejectCommand(cmd
, cmdCallback
);
1981 myUnforcedRequestFocusCmd
= null;
1984 private boolean checkForRejectOrByPass(final FocusCommand cmd
, final boolean forced
, final ActionCallback result
) {
1985 if (cmd
.isExpired()) {
1986 rejectCommand(cmd
, result
);
1990 final FocusCommand lastRequest
= getLastEffectiveForcedRequest();
1992 if (!forced
&& !isUnforcedRequestAllowed()) {
1993 if (cmd
.equals(lastRequest
)) {
1997 rejectCommand(cmd
, result
);
2003 if (lastRequest
!= null && lastRequest
.dominatesOver(cmd
)) {
2004 rejectCommand(cmd
, result
);
2008 if (!myApp
.isActive() && !canExecuteOnInactiveApplication(cmd
)) {
2009 if (myCallbackOnActivation
!= null) {
2010 myCallbackOnActivation
.setRejected();
2013 myFocusCommandOnAppActivation
= cmd
;
2014 myCallbackOnActivation
= result
;
2022 private void rejectCommand(FocusCommand cmd
, ActionCallback callback
) {
2024 resetUnforcedCommand(cmd
);
2026 callback
.setRejected();
2029 private void setCommand(FocusCommand command
) {
2030 myRequestFocusCmd
= command
;
2032 if (!myFocusRequests
.contains(command
)) {
2033 myFocusRequests
.add(command
);
2037 private void resetCommand(FocusCommand cmd
) {
2038 if (cmd
== myRequestFocusCmd
) {
2039 myRequestFocusCmd
= null;
2042 final KeyEventProcessor processor
= cmd
.getProcessor();
2043 if (processor
!= null) {
2044 processor
.finish(myKeyProcessorContext
);
2047 myFocusRequests
.remove(cmd
);
2050 private void resetUnforcedCommand(FocusCommand cmd
) {
2051 myUnforcedRequestFocusCmd
= null;
2052 myFocusRequests
.remove(cmd
);
2055 private boolean canExecuteOnInactiveApplication(FocusCommand cmd
) {
2056 return !Patches
.REQUEST_FOCUS_MAY_ACTIVATE_APP
|| cmd
.canExecuteOnInactiveApp();
2059 private void setLastEffectiveForcedRequest(FocusCommand command
) {
2060 myLastForcedRequest
= new WeakReference
<FocusCommand
>(command
);
2064 private FocusCommand
getLastEffectiveForcedRequest() {
2065 if (myLastForcedRequest
== null) return null;
2066 final FocusCommand request
= myLastForcedRequest
.get();
2067 return request
!= null && !request
.isExpired() ? request
: null;
2070 private boolean isUnforcedRequestAllowed() {
2071 return getLastEffectiveForcedRequest() == null;
2074 private boolean isProjectComponent(Component c
) {
2075 final Component frame
= UIUtil
.findUltimateParent(c
);
2076 if (frame
instanceof IdeFrame
) {
2077 return frame
== myWindowManager
.getFrame(myProject
);
2084 private class AppListener
extends ApplicationAdapter
{
2087 public void applicationDeactivated(IdeFrame ideFrame
) {
2089 final Component owner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
2090 if (isProjectComponent(owner
)) {
2093 c
= getLastFocusedProjectComponent();
2096 myFocusedComponentOnDeactivation
= c
!= null ?
new WeakReference
<Component
>(c
) : null;
2100 public void applicationActivated(IdeFrame ideFrame
) {
2101 final FocusCommand cmd
= myFocusCommandOnAppActivation
;
2102 ActionCallback callback
= myCallbackOnActivation
;
2103 myFocusCommandOnAppActivation
= null;
2104 myCallbackOnActivation
= null;
2105 if (cmd
!= null && !cmd
.isExpired()) {
2106 requestFocus(cmd
, true).notifyWhenDone(callback
);
2109 final KeyboardFocusManager mgr
= KeyboardFocusManager
.getCurrentKeyboardFocusManager();
2111 if (ideFrame
== myWindowManager
.getFrame(myProject
)) {
2112 final Component owner
= mgr
.getFocusOwner();
2113 Component old
= myFocusedComponentOnDeactivation
!= null ? myFocusedComponentOnDeactivation
.get() : null;
2115 if (owner
== null && old
!= null && old
.isShowing()) {
2116 requestFocus(old
, false);
2118 myFocusedComponentOnDeactivation
= null;
2124 private static class EdtAlarm
{
2125 private Alarm myAlarm
;
2127 public EdtAlarm(Disposable parent
) {
2128 myAlarm
= new Alarm(Alarm
.ThreadToUse
.OWN_THREAD
, parent
);
2131 public int getActiveRequestCount() {
2132 return myAlarm
.getActiveRequestCount();
2135 public void cancelAllRequests() {
2136 myAlarm
.cancelAllRequests();
2139 public void addRequest(EdtRunnable runnable
, int delay
) {
2140 myAlarm
.addRequest(runnable
, delay
);
2144 private class KeyProcessorConext
implements KeyEventProcessor
.Context
{
2145 public List
<KeyEvent
> getQueue() {
2146 return myToDispatchOnDone
;
2149 public void dispatch(final List
<KeyEvent
> events
) {
2150 doWhenFocusSettlesDown(new Runnable() {
2152 myToDispatchOnDone
.addAll(events
);