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
.components
.ProjectComponent
;
12 import com
.intellij
.openapi
.diagnostic
.Logger
;
13 import com
.intellij
.openapi
.extensions
.Extensions
;
14 import com
.intellij
.openapi
.fileEditor
.ex
.FileEditorManagerEx
;
15 import com
.intellij
.openapi
.project
.DumbAwareRunnable
;
16 import com
.intellij
.openapi
.project
.DumbService
;
17 import com
.intellij
.openapi
.project
.Project
;
18 import com
.intellij
.openapi
.startup
.StartupManager
;
19 import com
.intellij
.openapi
.ui
.MessageType
;
20 import com
.intellij
.openapi
.ui
.popup
.Balloon
;
21 import com
.intellij
.openapi
.ui
.popup
.JBPopupFactory
;
22 import com
.intellij
.openapi
.util
.*;
23 import com
.intellij
.openapi
.util
.registry
.Registry
;
24 import com
.intellij
.openapi
.wm
.*;
25 import com
.intellij
.openapi
.wm
.ex
.*;
26 import com
.intellij
.openapi
.wm
.impl
.commands
.*;
27 import com
.intellij
.ui
.awt
.RelativePoint
;
28 import com
.intellij
.util
.Alarm
;
29 import com
.intellij
.util
.ArrayUtil
;
30 import com
.intellij
.util
.ReflectionUtil
;
31 import com
.intellij
.util
.containers
.CollectionFactory
;
32 import com
.intellij
.util
.containers
.HashMap
;
33 import com
.intellij
.util
.containers
.HashSet
;
34 import com
.intellij
.util
.ui
.UIUtil
;
35 import com
.intellij
.util
.ui
.update
.UiNotifyConnector
;
36 import gnu
.trove
.THashSet
;
37 import org
.jdom
.Element
;
38 import org
.jetbrains
.annotations
.NonNls
;
39 import org
.jetbrains
.annotations
.NotNull
;
40 import org
.jetbrains
.annotations
.Nullable
;
43 import javax
.swing
.event
.EventListenerList
;
44 import javax
.swing
.event
.HyperlinkListener
;
46 import java
.awt
.event
.FocusEvent
;
47 import java
.awt
.event
.KeyEvent
;
48 import java
.beans
.PropertyChangeEvent
;
49 import java
.beans
.PropertyChangeListener
;
50 import java
.lang
.ref
.WeakReference
;
51 import java
.lang
.reflect
.Method
;
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
);
182 private Component
getLastFocusedProjectComponent() {
183 return myLastFocusedProjectComponent
!= null ? myLastFocusedProjectComponent
.get() : null;
186 public Project
getProject() {
190 public void initComponent() {
193 public void disposeComponent() {
194 myApp
.removeApplicationListener(myAppListener
);
197 public void projectOpened() {
198 UIManager
.addPropertyChangeListener(myUIManagerPropertyChangeListener
);
199 LafManager
.getInstance().addLafManagerListener(myLafManagerListener
);
201 myFrame
= myWindowManager
.allocateFrame(myProject
);
202 LOG
.assertTrue(myFrame
!= null);
204 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
206 myToolWindowsPane
= new ToolWindowsPane(myFrame
, this);
207 ((IdeRootPane
)myFrame
.getRootPane()).setToolWindowsPane(myToolWindowsPane
);
208 appendUpdateToolWindowsPaneCmd(commandsList
);
210 myFrame
.setTitle(FrameTitleBuilder
.getInstance().getProjectTitle(myProject
));
212 final JComponent editorComponent
= FileEditorManagerEx
.getInstanceEx(myProject
).getComponent();
213 myEditorComponentFocusWatcher
.install(editorComponent
);
215 appendSetEditorComponentCmd(editorComponent
, commandsList
);
216 if (myEditorComponentActive
) {
217 activateEditorComponentImpl(commandsList
, true);
219 execute(commandsList
);
221 final DumbService
.DumbModeListener dumbModeListener
= new DumbService
.DumbModeListener() {
222 private final Set
<String
> hiddenIds
= new THashSet
<String
>();
224 public void enteredDumbMode() {
227 public void beforeEnteringDumbMode() {
228 for (final String id
: getToolWindowIds()) {
229 if (!myDumbAwareIds
.contains(id
)) {
230 if (isToolWindowVisible(id
)) {
232 hideToolWindow(id
, true);
234 getStripeButton(id
).setEnabled(false);
239 public void exitDumbMode() {
240 for (final String id
: getToolWindowIds()) {
241 getStripeButton(id
).setEnabled(true);
243 for (final String id
: hiddenIds
) {
249 myProject
.getMessageBus().connect().subscribe(DumbService
.DUMB_MODE
, dumbModeListener
);
251 StartupManager
.getInstance(myProject
).registerPostStartupActivity(new DumbAwareRunnable() {
253 registerToolWindowsFromBeans();
254 if (DumbService
.getInstance().isDumb()) {
255 dumbModeListener
.beforeEnteringDumbMode();
261 private void registerToolWindowsFromBeans() {
262 ToolWindowEP
[] beans
= Extensions
.getExtensions(ToolWindowEP
.EP_NAME
);
263 for (final ToolWindowEP bean
: beans
) {
264 final Condition condition
= bean
.getCondition();
265 if (condition
!= null && !condition
.value(myProject
)) {
268 ToolWindowAnchor toolWindowAnchor
;
270 toolWindowAnchor
= ToolWindowAnchor
.fromText(bean
.anchor
);
272 catch (Exception e
) {
276 JLabel label
= new JLabel("Initializing toolwindow...");
277 final ToolWindow toolWindow
= registerToolWindow(bean
.id
, label
, toolWindowAnchor
, myProject
);
278 final ToolWindowFactory factory
= bean
.getToolWindowFactory();
279 if (bean
.icon
!= null) {
280 Icon icon
= IconLoader
.findIcon(bean
.icon
, factory
.getClass());
283 icon
= IconLoader
.getIcon(bean
.icon
);
284 } catch (Exception ignored
) {}
286 toolWindow
.setIcon(icon
);
288 toolWindow
.setSplitMode(bean
.secondary
, null);
289 UiNotifyConnector
.doWhenFirstShown(label
, new Runnable() {
291 ApplicationManager
.getApplication().invokeLater(new Runnable() {
293 toolWindow
.getContentManager().removeAllContents(false);
294 factory
.createToolWindowContent(myProject
, toolWindow
);
302 public void projectClosed() {
303 UIManager
.removePropertyChangeListener(myUIManagerPropertyChangeListener
);
304 LafManager
.getInstance().removeLafManagerListener(myLafManagerListener
);
305 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
306 final String
[] ids
= getToolWindowIds();
308 // Remove ToolWindowsPane
310 ((IdeRootPane
)myFrame
.getRootPane()).setToolWindowsPane(null);
311 myWindowManager
.releaseFrame(myFrame
);
312 appendUpdateToolWindowsPaneCmd(commandsList
);
314 // Hide all tool windows
316 for (final String id
: ids
) {
317 deactivateToolWindowImpl(id
, true, commandsList
);
320 // Remove editor component
322 final JComponent editorComponent
= FileEditorManagerEx
.getInstanceEx(myProject
).getComponent();
323 myEditorComponentFocusWatcher
.deinstall(editorComponent
);
324 appendSetEditorComponentCmd(null, commandsList
);
325 execute(commandsList
);
328 public void addToolWindowManagerListener(final ToolWindowManagerListener l
) {
329 myListenerList
.add(ToolWindowManagerListener
.class, l
);
332 public void removeToolWindowManagerListener(final ToolWindowManagerListener l
) {
333 myListenerList
.remove(ToolWindowManagerListener
.class, l
);
337 * This is helper method. It delegated its fuctionality to the WindowManager.
338 * Before delegating it fires state changed.
340 private void execute(final ArrayList
<FinalizableCommand
> commandList
) {
342 for (FinalizableCommand each
: commandList
) {
343 each
.beforeExecute(this);
345 myWindowManager
.getCommandProcessor().execute(commandList
, myProject
.getDisposed());
348 public void activateEditorComponent() {
349 activateEditorComponent(true);
352 private void activateEditorComponent(boolean forced
) {
353 if (LOG
.isDebugEnabled()) {
354 LOG
.debug("enter: activateEditorComponent()");
356 ApplicationManager
.getApplication().assertIsDispatchThread();
357 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
358 activateEditorComponentImpl(commandList
, forced
);
359 execute(commandList
);
362 private void activateEditorComponentImpl(final ArrayList
<FinalizableCommand
> commandList
, final boolean forced
) {
363 final String active
= getActiveToolWindowId();
364 // Now we have to request focus into most recent focused editor
365 appendRequestFocusInEditorComponentCmd(commandList
, forced
).doWhenDone(new Runnable() {
367 final ArrayList
<FinalizableCommand
> postExecute
= new ArrayList
<FinalizableCommand
>();
369 if (LOG
.isDebugEnabled()) {
370 LOG
.debug("editor activated");
372 deactivateWindows(postExecute
, null);
373 myActiveStack
.clear();
374 myEditorComponentActive
= true;
376 execute(postExecute
);
378 }).doWhenRejected(new Runnable() {
381 requestFocus(new FocusCommand() {
382 public ActionCallback
run() {
383 final ArrayList
<FinalizableCommand
> cmds
= new ArrayList
<FinalizableCommand
>();
385 final WindowInfoImpl toReactivate
= getInfo(active
);
386 final boolean reactivateLastActive
= toReactivate
!= null && !isToHide(toReactivate
);
387 deactivateWindows(cmds
, reactivateLastActive ? active
: null);
390 if (reactivateLastActive
) {
391 activateToolWindow(active
, false, true);
394 if (active
!= null) {
395 myActiveStack
.remove(active
, false);
398 if (!myActiveStack
.isEmpty()) {
399 activateToolWindow(myActiveStack
.peek(), false, true);
402 return new ActionCallback
.Done();
410 private void deactivateWindows(final ArrayList
<FinalizableCommand
> postExecute
, String idToIgnore
) {
411 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
412 for (final WindowInfoImpl info
: infos
) {
413 final boolean shouldHide
= isToHide(info
);
414 if (idToIgnore
!= null && idToIgnore
.equals(info
.getId())) {
417 deactivateToolWindowImpl(info
.getId(), shouldHide
, postExecute
);
421 private boolean isToHide(final WindowInfoImpl info
) {
422 return (info
.isAutoHide() || info
.isSliding()) && !(info
.isFloating() && hasModalChild(info
));
426 * Helper method. It makes window visible, activates it and request focus into the tool window.
427 * But it doesn't deactivate other tool windows. Use <code>prepareForActivation</code> method to
428 * deactivates other tool windows.
430 * @param dirtyMode if <code>true</code> then all UI operations are performed in "dirty" mode.
431 * It means that UI isn't validated and repainted just after each add/remove operation.
432 * @see ToolWindowManagerImpl#prepareForActivation
434 private void showAndActivate(final String id
,
435 final boolean dirtyMode
,
436 final ArrayList
<FinalizableCommand
> commandsList
,
437 boolean autoFocusContents
) {
438 if (!getToolWindow(id
).isAvailable()) {
442 final WindowInfoImpl info
= getInfo(id
);
443 boolean toApplyInfo
= false;
444 if (!info
.isActive()) {
445 info
.setActive(true);
448 showToolWindowImpl(id
, dirtyMode
, commandsList
);
452 appendApplyWindowInfoCmd(info
, commandsList
);
453 myActiveStack
.push(id
);
454 myEditorComponentActive
= false;
457 if (autoFocusContents
) {
458 appendRequestFocusInToolWindowCmd(id
, commandsList
, true);
462 void activateToolWindow(final String id
, boolean forced
, boolean autoFocusContents
) {
463 if (LOG
.isDebugEnabled()) {
464 LOG
.debug("enter: activateToolWindow(" + id
+ ")");
466 ApplicationManager
.getApplication().assertIsDispatchThread();
468 if (DumbService
.getInstance().isDumb() && !myDumbAwareIds
.contains(id
)) {
472 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
473 activateToolWindowImpl(id
, commandList
, forced
, autoFocusContents
);
474 execute(commandList
);
477 private void activateToolWindowImpl(final String id
,
478 final ArrayList
<FinalizableCommand
> commandList
,
480 boolean autoFocusContents
) {
481 if (!isUnforcedRequestAllowed() && !forced
) return;
483 if (LOG
.isDebugEnabled()) {
484 LOG
.debug("enter: activateToolWindowImpl(" + id
+ ")");
486 if (!getToolWindow(id
).isAvailable()) {
487 // Tool window can be "logically" active but not focused. For example,
488 // when the user switched to another application. So we just need to bring
489 // tool window's window to front.
490 final InternalDecorator decorator
= getInternalDecorator(id
);
491 if (!decorator
.hasFocus() && autoFocusContents
) {
492 appendRequestFocusInToolWindowCmd(id
, commandList
, forced
);
496 prepareForActivation(id
, commandList
);
497 showAndActivate(id
, false, commandList
, autoFocusContents
);
501 * Checkes whether the specified <code>id</code> defines installed tool
502 * window. If it's not then throws <code>IllegalStateException</code>.
504 * @throws IllegalStateException if tool window isn't installed.
506 private void checkId(final String id
) {
507 if (!myLayout
.isToolWindowRegistered(id
)) {
508 throw new IllegalStateException("window with id=\"" + id
+ "\" isn't registered");
513 * Helper method. It deactivates (and hides) window with specified <code>id</code>.
515 * @param id <code>id</code> of the tool window to be deactivated.
516 * @param shouldHide if <code>true</code> then also hides specified tool window.
518 private void deactivateToolWindowImpl(final String id
, final boolean shouldHide
, final List
<FinalizableCommand
> commandsList
) {
519 if (LOG
.isDebugEnabled()) {
520 LOG
.debug("enter: deactivateToolWindowImpl(" + id
+ "," + shouldHide
+ ")");
522 final WindowInfoImpl info
= getInfo(id
);
523 if (shouldHide
&& info
.isVisible()) {
524 info
.setVisible(false);
525 if (info
.isFloating()) {
526 appendRemoveFloatingDecoratorCmd(info
, commandsList
);
528 else { // docked and sliding windows
529 appendRemoveDecoratorCmd(id
, false, commandsList
);
532 info
.setActive(false);
533 appendApplyWindowInfoCmd(info
, commandsList
);
536 public String
[] getToolWindowIds() {
537 ApplicationManager
.getApplication().assertIsDispatchThread();
538 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
539 final String
[] ids
= ArrayUtil
.newStringArray(infos
.length
);
540 for (int i
= 0; i
< infos
.length
; i
++) {
541 ids
[i
] = infos
[i
].getId();
546 public String
getActiveToolWindowId() {
547 ApplicationManager
.getApplication().assertIsDispatchThread();
548 return myLayout
.getActiveId();
551 public String
getLastActiveToolWindowId() {
552 return getLastActiveToolWindowId(null);
555 public String
getLastActiveToolWindowId(Condition
<JComponent
> condition
) {
556 ApplicationManager
.getApplication().assertIsDispatchThread();
557 String lastActiveToolWindowId
= null;
558 for (int i
= 0; i
< myActiveStack
.getPersistentSize(); i
++) {
559 final String id
= myActiveStack
.peekPersistent(i
);
560 final ToolWindow toolWindow
= getToolWindow(id
);
561 LOG
.assertTrue(toolWindow
!= null);
562 if (toolWindow
.isAvailable()) {
563 if (condition
== null || condition
.value(toolWindow
.getComponent())) {
564 lastActiveToolWindowId
= id
;
569 return lastActiveToolWindowId
;
573 * @return floating decorator for the tool window with specified <code>ID</code>.
575 private FloatingDecorator
getFloatingDecorator(final String id
) {
576 return myId2FloatingDecorator
.get(id
);
580 * @return internal decorator for the tool window with specified <code>ID</code>.
582 private InternalDecorator
getInternalDecorator(final String id
) {
583 return myId2InternalDecorator
.get(id
);
587 * @return tool button for the window with specified <code>ID</code>.
589 private StripeButton
getStripeButton(final String id
) {
590 return myId2StripeButton
.get(id
);
594 * @return info for the tool window with specified <code>ID</code>.
596 private WindowInfoImpl
getInfo(final String id
) {
597 return myLayout
.getInfo(id
, true);
600 public List
<String
> getIdsOn(final ToolWindowAnchor anchor
) {
601 return myLayout
.getVisibleIdsOn(anchor
, this);
604 public ToolWindow
getToolWindow(final String id
) {
605 ApplicationManager
.getApplication().assertIsDispatchThread();
606 if (!myLayout
.isToolWindowRegistered(id
)) {
609 return getInternalDecorator(id
).getToolWindow();
612 void showToolWindow(final String id
) {
613 if (LOG
.isDebugEnabled()) {
614 LOG
.debug("enter: showToolWindow(" + id
+ ")");
616 ApplicationManager
.getApplication().assertIsDispatchThread();
617 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
618 showToolWindowImpl(id
, false, commandList
);
619 execute(commandList
);
622 public void hideToolWindow(final String id
, final boolean hideSide
) {
623 ApplicationManager
.getApplication().assertIsDispatchThread();
625 final WindowInfoImpl info
= getInfo(id
);
626 if (!info
.isVisible()) return;
627 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
628 final boolean wasActive
= info
.isActive();
630 // hide and deactivate
632 deactivateToolWindowImpl(id
, true, commandList
);
634 if (hideSide
|| info
.isFloating()) {
635 final List
<String
> ids
= myLayout
.getVisibleIdsOn(info
.getAnchor(), this);
636 for (String each
: ids
) {
637 myActiveStack
.remove(each
, true);
641 while (!mySideStack
.isEmpty(info
.getAnchor())) {
642 mySideStack
.pop(info
.getAnchor());
645 final String
[] all
= getToolWindowIds();
646 for (String eachId
: all
) {
647 final WindowInfoImpl eachInfo
= getInfo(eachId
);
648 if (eachInfo
.isVisible() && eachInfo
.getAnchor() == info
.getAnchor()) {
649 deactivateToolWindowImpl(eachId
, true, commandList
);
653 activateEditorComponentImpl(commandList
, true);
657 // first of all we have to find tool window that was located at the same side and
660 WindowInfoImpl info2
= null;
661 while (!mySideStack
.isEmpty(info
.getAnchor())) {
662 final WindowInfoImpl storedInfo
= mySideStack
.pop(info
.getAnchor());
663 final WindowInfoImpl currentInfo
= getInfo(storedInfo
.getId());
664 LOG
.assertTrue(currentInfo
!= null);
665 // SideStack contains copies of real WindowInfos. It means that
666 // these stored infos can be invalid. The following loop removes invalid WindowInfos.
667 if (storedInfo
.getAnchor() == currentInfo
.getAnchor() &&
668 storedInfo
.getType() == currentInfo
.getType() &&
669 storedInfo
.isAutoHide() == currentInfo
.isAutoHide()) {
675 showToolWindowImpl(info2
.getId(), false, commandList
);
678 // If we hide currently active tool window then we should activate the previous
679 // one which is located in the tool window stack.
680 // Activate another tool window if no active tool window exists and
681 // window stack is enabled.
683 myActiveStack
.remove(id
, false); // hidden window should be at the top of stack
685 if (myActiveStack
.isEmpty()) {
686 activateEditorComponentImpl(commandList
, false);
689 final String toBeActivatedId
= myActiveStack
.pop();
690 if (toBeActivatedId
!= null) {
691 activateToolWindowImpl(toBeActivatedId
, commandList
, false, true);
697 execute(commandList
);
701 * @param dirtyMode if <code>true</code> then all UI operations are performed in dirty mode.
703 private void showToolWindowImpl(final String id
, final boolean dirtyMode
, final List
<FinalizableCommand
> commandsList
) {
704 final WindowInfoImpl toBeShownInfo
= getInfo(id
);
705 if (toBeShownInfo
.isVisible() || !getToolWindow(id
).isAvailable()) {
709 toBeShownInfo
.setVisible(true);
710 final InternalDecorator decorator
= getInternalDecorator(id
);
712 if (toBeShownInfo
.isFloating()) {
713 commandsList
.add(new AddFloatingDecoratorCmd(decorator
, toBeShownInfo
));
715 else { // docked and sliding windows
717 // If there is tool window on the same side then we have to hide it, i.e.
718 // clear place for tool window to be shown.
720 // We store WindowInfo of hidden tool window in the SideStack (if the tool window
721 // is docked and not auto-hide one). Therefore it's possible to restore the
722 // hidden tool window when showing tool window will be closed.
724 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
725 for (final WindowInfoImpl info
: infos
) {
726 if (id
.equals(info
.getId())) {
729 if (info
.isVisible() &&
730 info
.getType() == toBeShownInfo
.getType() &&
731 info
.getAnchor() == toBeShownInfo
.getAnchor() &&
732 info
.isSplit() == toBeShownInfo
.isSplit()) {
733 // hide and deactivate tool window
734 info
.setVisible(false);
735 appendRemoveDecoratorCmd(info
.getId(), false, commandsList
);
736 if (info
.isActive()) {
737 info
.setActive(false);
739 appendApplyWindowInfoCmd(info
, commandsList
);
740 // store WindowInfo into the SideStack
741 if (info
.isDocked() && !info
.isAutoHide()) {
742 mySideStack
.push(info
);
746 appendAddDecoratorCmd(decorator
, toBeShownInfo
, dirtyMode
, commandsList
);
748 // Remove tool window from the SideStack.
750 mySideStack
.remove(id
);
753 appendApplyWindowInfoCmd(toBeShownInfo
, commandsList
);
756 public ToolWindow
registerToolWindow(@NotNull final String id
,
757 @NotNull final JComponent component
,
758 @NotNull final ToolWindowAnchor anchor
) {
759 return registerToolWindow(id
, component
, anchor
, false);
762 public ToolWindow
registerToolWindow(@NotNull final String id
,
763 @NotNull JComponent component
,
764 @NotNull ToolWindowAnchor anchor
,
765 Disposable parentDisposable
) {
766 return registerToolWindow(id
, component
, anchor
, parentDisposable
, false);
769 public ToolWindow
registerToolWindow(@NotNull final String id
,
770 @NotNull JComponent component
,
771 @NotNull ToolWindowAnchor anchor
,
772 Disposable parentDisposable
,
773 boolean canWorkInDumbMode
) {
774 return registerDisposable(id
, parentDisposable
, registerToolWindow(id
, component
, anchor
, canWorkInDumbMode
));
777 private ToolWindow
registerToolWindow(@NotNull final String id
,
778 @NotNull final JComponent component
,
779 @NotNull final ToolWindowAnchor anchor
,
780 boolean canWorkInDumbMode
) {
781 return registerToolWindow(id
, component
, anchor
, false, false, canWorkInDumbMode
);
784 public ToolWindow
registerToolWindow(@NotNull final String id
, final boolean canCloseContent
, @NotNull final ToolWindowAnchor anchor
) {
785 return registerToolWindow(id
, null, anchor
, false, canCloseContent
, false);
788 public ToolWindow
registerToolWindow(@NotNull final String id
,
789 final boolean canCloseContent
,
790 @NotNull final ToolWindowAnchor anchor
,
791 final boolean sideTool
) {
792 return registerToolWindow(id
, null, anchor
, sideTool
, canCloseContent
, false);
796 public ToolWindow
registerToolWindow(@NotNull final String id
,
797 final boolean canCloseContent
,
798 @NotNull final ToolWindowAnchor anchor
,
799 final Disposable parentDisposable
,
800 final boolean canWorkInDumbMode
) {
801 return registerDisposable(id
, parentDisposable
, registerToolWindow(id
, null, anchor
, false, canCloseContent
, canWorkInDumbMode
));
804 private ToolWindow
registerToolWindow(@NotNull final String id
,
805 @Nullable final JComponent component
,
806 @NotNull final ToolWindowAnchor anchor
,
808 boolean canCloseContent
,
809 final boolean canWorkInDumbMode
) {
810 if (LOG
.isDebugEnabled()) {
811 LOG
.debug("enter: installToolWindow(" + id
+ "," + component
+ "," + anchor
+ "\")");
813 ApplicationManager
.getApplication().assertIsDispatchThread();
814 if (myLayout
.isToolWindowRegistered(id
)) {
815 throw new IllegalArgumentException("window with id=\"" + id
+ "\" is already registered");
818 final WindowInfoImpl info
= myLayout
.register(id
, anchor
, sideTool
);
819 final boolean wasActive
= info
.isActive();
820 final boolean wasVisible
= info
.isVisible();
821 info
.setActive(false);
822 info
.setVisible(false);
826 final ToolWindowImpl toolWindow
= new ToolWindowImpl(this, id
, canCloseContent
, component
);
827 final InternalDecorator decorator
= new InternalDecorator(myProject
, info
.copy(), toolWindow
);
828 myId2InternalDecorator
.put(id
, decorator
);
829 decorator
.addInternalDecoratorListener(myInternalDecoratorListener
);
830 toolWindow
.addPropertyChangeListener(myToolWindowPropertyChangeListener
);
831 myId2FocusWatcher
.put(id
, new ToolWindowFocusWatcher(toolWindow
));
833 if (canWorkInDumbMode
) {
834 myDumbAwareIds
.add(id
);
837 // Create and show tool button
839 final StripeButton button
= new StripeButton(decorator
, myToolWindowsPane
);
840 myId2StripeButton
.put(id
, button
);
841 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
842 appendAddButtonCmd(button
, info
, commandsList
);
844 // If preloaded info is visible or active then we have to show/activate the installed
845 // tool window. This step has sense only for windows which are not in the autohide
846 // mode. But if tool window was active but its mode doen't allow to activate it again
847 // (for example, tool window is in autohide mode) then we just activate editor component.
849 if (!info
.isAutoHide() && (info
.isDocked() || info
.isFloating())) {
851 activateToolWindowImpl(info
.getId(), commandsList
, true, true);
853 else if (wasVisible
) {
854 showToolWindowImpl(info
.getId(), false, commandsList
);
857 else if (wasActive
) { // tool window was active but it cannot be activate again
858 activateEditorComponentImpl(commandsList
, true);
861 execute(commandsList
);
862 fireToolWindowRegistered(id
);
866 private ToolWindow
registerDisposable(final String id
, final Disposable parentDisposable
, final ToolWindow window
) {
867 Disposer
.register(parentDisposable
, new Disposable() {
868 public void dispose() {
869 unregisterToolWindow(id
);
875 public void unregisterToolWindow(@NotNull final String id
) {
876 if (LOG
.isDebugEnabled()) {
877 LOG
.debug("enter: unregisterToolWindow(" + id
+ ")");
879 ApplicationManager
.getApplication().assertIsDispatchThread();
880 if (!myLayout
.isToolWindowRegistered(id
)) {
884 final WindowInfoImpl info
= getInfo(id
);
885 final ToolWindowEx toolWindow
= (ToolWindowEx
)getToolWindow(id
);
886 // Save recent appearance of tool window
887 myLayout
.unregister(id
);
888 // Remove decorator and tool button from the screen
889 final ArrayList
<FinalizableCommand
> commandsList
= new ArrayList
<FinalizableCommand
>();
890 if (info
.isVisible()) {
891 info
.setVisible(false);
892 if (info
.isFloating()) {
893 appendRemoveFloatingDecoratorCmd(info
, commandsList
);
895 else { // floating and sliding windows
896 appendRemoveDecoratorCmd(id
, false, commandsList
);
899 appendRemoveButtonCmd(id
, commandsList
);
900 appendApplyWindowInfoCmd(info
, commandsList
);
901 execute(commandsList
);
902 // Remove all references on tool window and save its last properties
903 toolWindow
.removePropertyChangeListener(myToolWindowPropertyChangeListener
);
904 myActiveStack
.remove(id
, true);
905 mySideStack
.remove(id
);
906 // Destroy stripe button
907 final StripeButton button
= getStripeButton(id
);
909 myId2StripeButton
.remove(id
);
911 myId2FocusWatcher
.remove(id
);
913 final InternalDecorator decorator
= getInternalDecorator(id
);
915 decorator
.removeInternalDecoratorListener(myInternalDecoratorListener
);
916 myId2InternalDecorator
.remove(id
);
919 public DesktopLayout
getLayout() {
920 ApplicationManager
.getApplication().assertIsDispatchThread();
924 public void setLayoutToRestoreLater(DesktopLayout layout
) {
925 myLayoutToRestoreLater
= layout
;
928 public DesktopLayout
getLayoutToRestoreLater() {
929 return myLayoutToRestoreLater
;
932 public void setLayout(final DesktopLayout layout
) {
933 ApplicationManager
.getApplication().assertIsDispatchThread();
934 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
935 // hide tool window that are invisible in new layout
936 final WindowInfoImpl
[] currentInfos
= myLayout
.getInfos();
937 for (final WindowInfoImpl currentInfo
: currentInfos
) {
938 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
942 if (currentInfo
.isVisible() && !info
.isVisible()) {
943 deactivateToolWindowImpl(currentInfo
.getId(), true, commandList
);
946 // change anchor of tool windows
947 for (final WindowInfoImpl currentInfo
: currentInfos
) {
948 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
952 if (currentInfo
.getAnchor() != info
.getAnchor() || currentInfo
.getOrder() != info
.getOrder()) {
953 setToolWindowAnchorImpl(currentInfo
.getId(), info
.getAnchor(), info
.getOrder(), commandList
);
956 // change types of tool windows
957 for (final WindowInfoImpl currentInfo
: currentInfos
) {
958 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
962 if (currentInfo
.getType() != info
.getType()) {
963 setToolWindowTypeImpl(currentInfo
.getId(), info
.getType(), commandList
);
966 // change auto-hide state
967 for (final WindowInfoImpl currentInfo
: currentInfos
) {
968 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
972 if (currentInfo
.isAutoHide() != info
.isAutoHide()) {
973 setToolWindowAutoHideImpl(currentInfo
.getId(), info
.isAutoHide(), commandList
);
976 // restore visibility
977 for (final WindowInfoImpl currentInfo
: currentInfos
) {
978 final WindowInfoImpl info
= layout
.getInfo(currentInfo
.getId(), false);
982 if (info
.isVisible()) {
983 showToolWindowImpl(currentInfo
.getId(), false, commandList
);
986 // if there is no any active tool window and editor is also inactive
987 // then activate editor
988 if (!myEditorComponentActive
&& getActiveToolWindowId() == null) {
989 activateEditorComponentImpl(commandList
, true);
991 execute(commandList
);
994 public void invokeLater(final Runnable runnable
) {
995 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
996 commandList
.add(new InvokeLaterCmd(runnable
, myWindowManager
.getCommandProcessor()));
997 execute(commandList
);
1000 public IdeFocusManager
getFocusManager() {
1001 return IdeFocusManager
.getInstance(myProject
);
1005 public void notifyByBalloon(@NotNull final String toolWindowId
, @NotNull final MessageType type
, @NotNull final String htmlBody
) {
1006 notifyByBalloon(toolWindowId
, type
, htmlBody
, null, null);
1009 public void notifyByBalloon(@NotNull final String toolWindowId
,
1010 final MessageType type
,
1011 @NotNull final String text
,
1012 @Nullable final Icon icon
,
1013 @Nullable HyperlinkListener listener
) {
1014 checkId(toolWindowId
);
1016 final Stripe stripe
= myToolWindowsPane
.getStripeFor(toolWindowId
);
1017 final ToolWindowImpl window
= getInternalDecorator(toolWindowId
).getToolWindow();
1018 if (!window
.isAvailable()) {
1019 window
.setPlaceholderMode(true);
1020 stripe
.updateState();
1021 stripe
.revalidate();
1025 final ToolWindowAnchor anchor
= getInfo(toolWindowId
).getAnchor();
1026 final Ref
<Balloon
.Position
> position
= Ref
.create(Balloon
.Position
.below
);
1027 if (ToolWindowAnchor
.TOP
== anchor
) {
1028 position
.set(Balloon
.Position
.below
);
1030 else if (ToolWindowAnchor
.BOTTOM
== anchor
) {
1031 position
.set(Balloon
.Position
.above
);
1033 else if (ToolWindowAnchor
.LEFT
== anchor
) {
1034 position
.set(Balloon
.Position
.atRight
);
1036 else if (ToolWindowAnchor
.RIGHT
== anchor
) {
1037 position
.set(Balloon
.Position
.atLeft
);
1040 Icon actualIcon
= icon
!= null ? icon
: type
.getDefaultIcon();
1042 final Balloon balloon
=
1043 JBPopupFactory
.getInstance().createHtmlTextBalloonBuilder(text
.replace("\n", "<br>"), actualIcon
, type
.getPopupBackground(), listener
)
1045 Disposer
.register(balloon
, new Disposable() {
1046 public void dispose() {
1047 window
.setPlaceholderMode(false);
1048 stripe
.updateState();
1049 stripe
.revalidate();
1054 final StripeButton button
= stripe
.getButtonFor(toolWindowId
);
1055 if (button
== null) return;
1057 final Runnable show
= new Runnable() {
1059 if (button
.isShowing()) {
1060 final Point point
= new Point(button
.getBounds().width
/ 2, button
.getHeight() / 2 - 2);
1061 balloon
.show(new RelativePoint(button
, point
), position
.get());
1064 final Rectangle bounds
= myToolWindowsPane
.getBounds();
1065 final Point target
= UIUtil
.getCenterPoint(bounds
, new Dimension(1, 1));
1066 if (ToolWindowAnchor
.TOP
== anchor
) {
1069 else if (ToolWindowAnchor
.BOTTOM
== anchor
) {
1070 target
.y
= bounds
.height
;
1072 else if (ToolWindowAnchor
.LEFT
== anchor
) {
1075 else if (ToolWindowAnchor
.RIGHT
== anchor
) {
1076 target
.x
= bounds
.width
;
1079 balloon
.show(new RelativePoint(myToolWindowsPane
, target
), position
.get());
1084 if (!button
.isValid()) {
1085 SwingUtilities
.invokeLater(new Runnable() {
1097 public boolean isEditorComponentActive() {
1098 ApplicationManager
.getApplication().assertIsDispatchThread();
1099 return myEditorComponentActive
;
1102 ToolWindowAnchor
getToolWindowAnchor(final String id
) {
1103 ApplicationManager
.getApplication().assertIsDispatchThread();
1105 return getInfo(id
).getAnchor();
1108 void setToolWindowAnchor(final String id
, final ToolWindowAnchor anchor
) {
1109 ApplicationManager
.getApplication().assertIsDispatchThread();
1110 setToolWindowAnchor(id
, anchor
, -1);
1113 void setToolWindowAnchor(final String id
, final ToolWindowAnchor anchor
, final int order
) {
1114 ApplicationManager
.getApplication().assertIsDispatchThread();
1115 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1116 setToolWindowAnchorImpl(id
, anchor
, order
, commandList
);
1117 execute(commandList
);
1120 private void setToolWindowAnchorImpl(final String id
,
1121 final ToolWindowAnchor anchor
,
1123 final ArrayList
<FinalizableCommand
> commandsList
) {
1125 final WindowInfoImpl info
= getInfo(id
);
1126 if (anchor
== info
.getAnchor() && order
== info
.getOrder()) {
1129 // if tool window isn't visible or only order number is changed then just remove/add stripe button
1130 if (!info
.isVisible() || anchor
== info
.getAnchor() || info
.isFloating()) {
1131 appendRemoveButtonCmd(id
, commandsList
);
1132 myLayout
.setAnchor(id
, anchor
, order
);
1133 // update infos for all window. Actually we have to update only infos affected by
1135 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1136 for (WindowInfoImpl info1
: infos
) {
1137 appendApplyWindowInfoCmd(info1
, commandsList
);
1139 appendAddButtonCmd(getStripeButton(id
), info
, commandsList
);
1141 else { // for docked and sliding windows we have to move buttons and window's decorators
1142 info
.setVisible(false);
1143 appendRemoveDecoratorCmd(id
, false, commandsList
);
1144 appendRemoveButtonCmd(id
, commandsList
);
1145 myLayout
.setAnchor(id
, anchor
, order
);
1146 // update infos for all window. Actually we have to update only infos affected by
1148 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1149 for (WindowInfoImpl info1
: infos
) {
1150 appendApplyWindowInfoCmd(info1
, commandsList
);
1152 appendAddButtonCmd(getStripeButton(id
), info
, commandsList
);
1153 showToolWindowImpl(id
, false, commandsList
);
1154 if (info
.isActive()) {
1155 appendRequestFocusInToolWindowCmd(id
, commandsList
, true);
1160 boolean isSplitMode(String id
) {
1161 ApplicationManager
.getApplication().assertIsDispatchThread();
1163 return getInfo(id
).isSplit();
1166 void setSideTool(String id
, boolean isSide
) {
1167 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1168 setSplitModeImpl(id
, isSide
, commandList
);
1169 execute(commandList
);
1172 void setSideToolAndAnchor(String id
, ToolWindowAnchor anchor
, int order
, boolean isSide
) {
1173 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1174 setToolWindowAnchor(id
, anchor
, order
);
1175 setSplitModeImpl(id
, isSide
, commandList
);
1176 execute(commandList
);
1179 private void setSplitModeImpl(final String id
, final boolean isSplit
, final ArrayList
<FinalizableCommand
> commandList
) {
1181 final WindowInfoImpl info
= getInfo(id
);
1182 if (isSplit
== info
.isSplit()) {
1186 myLayout
.setSplitMode(id
, isSplit
);
1188 boolean wasActive
= info
.isActive();
1190 deactivateToolWindowImpl(id
, true, commandList
);
1192 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1193 for (WindowInfoImpl info1
: infos
) {
1194 appendApplyWindowInfoCmd(info1
, commandList
);
1197 activateToolWindowImpl(id
, commandList
, true, true);
1199 commandList
.add(myToolWindowsPane
.createUpdateButtonPositionCmd(id
, myWindowManager
.getCommandProcessor()));
1202 ToolWindowType
getToolWindowInternalType(final String id
) {
1203 ApplicationManager
.getApplication().assertIsDispatchThread();
1205 return getInfo(id
).getInternalType();
1208 ToolWindowType
getToolWindowType(final String id
) {
1209 ApplicationManager
.getApplication().assertIsDispatchThread();
1211 return getInfo(id
).getType();
1214 private void fireToolWindowRegistered(final String id
) {
1215 final ToolWindowManagerListener
[] listeners
= myListenerList
.getListeners(ToolWindowManagerListener
.class);
1216 for (ToolWindowManagerListener listener
: listeners
) {
1217 listener
.toolWindowRegistered(id
);
1221 private void fireStateChanged() {
1222 final ToolWindowManagerListener
[] listeners
= myListenerList
.getListeners(ToolWindowManagerListener
.class);
1223 for (ToolWindowManagerListener listener
: listeners
) {
1224 listener
.stateChanged();
1228 boolean isToolWindowActive(final String id
) {
1229 ApplicationManager
.getApplication().assertIsDispatchThread();
1231 return getInfo(id
).isActive();
1234 boolean isToolWindowAutoHide(final String id
) {
1235 ApplicationManager
.getApplication().assertIsDispatchThread();
1237 return getInfo(id
).isAutoHide();
1240 public boolean isToolWindowFloating(final String id
) {
1241 ApplicationManager
.getApplication().assertIsDispatchThread();
1243 return getInfo(id
).isFloating();
1246 boolean isToolWindowVisible(final String id
) {
1247 ApplicationManager
.getApplication().assertIsDispatchThread();
1249 return getInfo(id
).isVisible();
1252 void setToolWindowAutoHide(final String id
, final boolean autoHide
) {
1253 ApplicationManager
.getApplication().assertIsDispatchThread();
1254 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1255 setToolWindowAutoHideImpl(id
, autoHide
, commandList
);
1256 execute(commandList
);
1259 private void setToolWindowAutoHideImpl(final String id
, final boolean autoHide
, final ArrayList
<FinalizableCommand
> commandsList
) {
1261 final WindowInfoImpl info
= getInfo(id
);
1262 if (info
.isAutoHide() == autoHide
) {
1265 info
.setAutoHide(autoHide
);
1266 appendApplyWindowInfoCmd(info
, commandsList
);
1267 if (info
.isVisible()) {
1268 prepareForActivation(id
, commandsList
);
1269 showAndActivate(id
, false, commandsList
, true);
1273 void setToolWindowType(final String id
, final ToolWindowType type
) {
1274 ApplicationManager
.getApplication().assertIsDispatchThread();
1275 final ArrayList
<FinalizableCommand
> commandList
= new ArrayList
<FinalizableCommand
>();
1276 setToolWindowTypeImpl(id
, type
, commandList
);
1277 execute(commandList
);
1280 private void setToolWindowTypeImpl(final String id
, final ToolWindowType type
, final ArrayList
<FinalizableCommand
> commandsList
) {
1282 final WindowInfoImpl info
= getInfo(id
);
1283 if (info
.getType() == type
) {
1286 if (info
.isVisible()) {
1287 final boolean dirtyMode
= info
.isDocked() || info
.isSliding();
1288 info
.setVisible(false);
1289 if (info
.isFloating()) {
1290 appendRemoveFloatingDecoratorCmd(info
, commandsList
);
1292 else { // docked and sliding windows
1293 appendRemoveDecoratorCmd(id
, dirtyMode
, commandsList
);
1296 appendApplyWindowInfoCmd(info
, commandsList
);
1297 prepareForActivation(id
, commandsList
);
1298 showAndActivate(id
, dirtyMode
, commandsList
, true);
1299 appendUpdateToolWindowsPaneCmd(commandsList
);
1303 appendApplyWindowInfoCmd(info
, commandsList
);
1307 private void appendApplyWindowInfoCmd(final WindowInfoImpl info
, final List
<FinalizableCommand
> commandsList
) {
1308 final StripeButton button
= getStripeButton(info
.getId());
1309 final InternalDecorator decorator
= getInternalDecorator(info
.getId());
1310 commandsList
.add(new ApplyWindowInfoCmd(info
, button
, decorator
, myWindowManager
.getCommandProcessor()));
1314 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddDecoratorCmd
1316 private void appendAddDecoratorCmd(final InternalDecorator decorator
,
1317 final WindowInfoImpl info
,
1318 final boolean dirtyMode
,
1319 final List
<FinalizableCommand
> commandsList
) {
1320 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1321 final FinalizableCommand command
= myToolWindowsPane
.createAddDecoratorCmd(decorator
, info
, dirtyMode
, commandProcessor
);
1322 commandsList
.add(command
);
1326 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createRemoveDecoratorCmd
1328 private void appendRemoveDecoratorCmd(final String id
, final boolean dirtyMode
, final List
<FinalizableCommand
> commandsList
) {
1329 final FinalizableCommand command
= myToolWindowsPane
.createRemoveDecoratorCmd(id
, dirtyMode
, myWindowManager
.getCommandProcessor());
1330 commandsList
.add(command
);
1333 private void appendRemoveFloatingDecoratorCmd(final WindowInfoImpl info
, final List
<FinalizableCommand
> commandsList
) {
1334 final RemoveFloatingDecoratorCmd command
= new RemoveFloatingDecoratorCmd(info
);
1335 commandsList
.add(command
);
1339 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddButtonCmd
1341 private void appendAddButtonCmd(final StripeButton button
, final WindowInfoImpl info
, final List
<FinalizableCommand
> commandsList
) {
1342 final Comparator comparator
= myLayout
.comparator(info
.getAnchor());
1343 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1344 final FinalizableCommand command
= myToolWindowsPane
.createAddButtonCmd(button
, info
, comparator
, commandProcessor
);
1345 commandsList
.add(command
);
1349 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddButtonCmd
1351 private void appendRemoveButtonCmd(final String id
, final List
<FinalizableCommand
> commandsList
) {
1352 final FinalizableCommand command
= myToolWindowsPane
.createRemoveButtonCmd(id
, myWindowManager
.getCommandProcessor());
1353 commandsList
.add(command
);
1356 private ActionCallback
appendRequestFocusInEditorComponentCmd(final ArrayList
<FinalizableCommand
> commandList
, boolean forced
) {
1357 if (myProject
.isDisposed()) return new ActionCallback
.Done();
1358 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1359 final RequestFocusInEditorComponentCmd command
=
1360 new RequestFocusInEditorComponentCmd(FileEditorManagerEx
.getInstanceEx(myProject
), commandProcessor
, forced
);
1361 commandList
.add(command
);
1362 return command
.getDoneCallback();
1365 private void appendRequestFocusInToolWindowCmd(final String id
, final ArrayList
<FinalizableCommand
> commandList
, boolean forced
) {
1366 final ToolWindowImpl toolWindow
= (ToolWindowImpl
)getToolWindow(id
);
1367 final FocusWatcher focusWatcher
= myId2FocusWatcher
.get(id
);
1368 commandList
.add(new RequestFocusInToolWindowCmd(toolWindow
, focusWatcher
, myWindowManager
.getCommandProcessor(), forced
));
1372 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createSetEditorComponentCmd
1374 private void appendSetEditorComponentCmd(final JComponent component
, final List
<FinalizableCommand
> commandsList
) {
1375 final CommandProcessor commandProcessor
= myWindowManager
.getCommandProcessor();
1376 final FinalizableCommand command
= myToolWindowsPane
.createSetEditorComponentCmd(component
, commandProcessor
);
1377 commandsList
.add(command
);
1380 private void appendUpdateToolWindowsPaneCmd(final List
<FinalizableCommand
> commandsList
) {
1381 final JRootPane rootPane
= myFrame
.getRootPane();
1382 final FinalizableCommand command
= new UpdateRootPaneCmd(rootPane
, myWindowManager
.getCommandProcessor());
1383 commandsList
.add(command
);
1387 * @return <code>true</code> if tool window with the specified <code>id</code>
1388 * is floating and has modal showing child dialog. Such windows should not be closed
1389 * when auto-hide windows are gone.
1391 private boolean hasModalChild(final WindowInfoImpl info
) {
1392 if (!info
.isVisible() || !info
.isFloating()) {
1395 final FloatingDecorator decorator
= getFloatingDecorator(info
.getId());
1396 LOG
.assertTrue(decorator
!= null);
1397 return isModalOrHasModalChild(decorator
);
1400 private static boolean isModalOrHasModalChild(final Window window
) {
1401 if (window
instanceof Dialog
) {
1402 final Dialog dialog
= (Dialog
)window
;
1403 if (dialog
.isModal() && dialog
.isShowing()) {
1406 final Window
[] ownedWindows
= dialog
.getOwnedWindows();
1407 for (int i
= ownedWindows
.length
- 1; i
>= 0; i
--) {
1408 if (isModalOrHasModalChild(ownedWindows
[i
])) {
1417 * Helper method. It deactivates all tool windows excepting the tool window
1418 * which should be activated.
1420 private void prepareForActivation(final String id
, final List
<FinalizableCommand
> commandList
) {
1421 final WindowInfoImpl toBeActivatedInfo
= getInfo(id
);
1422 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1423 for (final WindowInfoImpl info
: infos
) {
1424 if (id
.equals(info
.getId())) {
1427 if (toBeActivatedInfo
.isDocked() || toBeActivatedInfo
.isSliding()) {
1428 deactivateToolWindowImpl(info
.getId(), info
.isAutoHide() || info
.isSliding(), commandList
);
1430 else { // floating window is being activated
1431 deactivateToolWindowImpl(info
.getId(), info
.isAutoHide() && info
.isFloating() && !hasModalChild(info
), commandList
);
1436 public void clearSideStack() {
1437 mySideStack
.clear();
1440 public void readExternal(final Element element
) {
1441 for (final Object o
: element
.getChildren()) {
1442 final Element e
= (Element
)o
;
1443 if (EDITOR_ELEMENT
.equals(e
.getName())) {
1444 myEditorComponentActive
= Boolean
.valueOf(e
.getAttributeValue(ACTIVE_ATTR_VALUE
)).booleanValue();
1446 else if (DesktopLayout
.TAG
.equals(e
.getName())) { // read layout of tool windows
1447 myLayout
.readExternal(e
);
1452 public void writeExternal(final Element element
) {
1453 if (myFrame
== null) {
1454 // do nothing if the project was not opened
1457 final String
[] ids
= getToolWindowIds();
1459 // Update size of all open floating windows. See SCR #18439
1460 for (final String id
: ids
) {
1461 final WindowInfoImpl info
= getInfo(id
);
1462 if (info
.isVisible()) {
1463 final InternalDecorator decorator
= getInternalDecorator(id
);
1464 LOG
.assertTrue(decorator
!= null);
1465 decorator
.fireResized();
1469 // Save frame's bounds
1470 final Rectangle frameBounds
= myFrame
.getBounds();
1471 final Element frameElement
= new Element(FRAME_ELEMENT
);
1472 element
.addContent(frameElement
);
1473 frameElement
.setAttribute(X_ATTR
, Integer
.toString(frameBounds
.x
));
1474 frameElement
.setAttribute(Y_ATTR
, Integer
.toString(frameBounds
.y
));
1475 frameElement
.setAttribute(WIDTH_ATTR
, Integer
.toString(frameBounds
.width
));
1476 frameElement
.setAttribute(HEIGHT_ATTR
, Integer
.toString(frameBounds
.height
));
1477 frameElement
.setAttribute(EXTENDED_STATE_ATTR
, Integer
.toString(myFrame
.getExtendedState()));
1478 // Save whether editor is active or not
1479 final Element editorElement
= new Element(EDITOR_ELEMENT
);
1480 editorElement
.setAttribute(ACTIVE_ATTR_VALUE
, myEditorComponentActive ? Boolean
.TRUE
.toString() : Boolean
.FALSE
.toString());
1481 element
.addContent(editorElement
);
1482 // Save layout of tool windows
1483 final Element layoutElement
= new Element(DesktopLayout
.TAG
);
1484 element
.addContent(layoutElement
);
1485 myLayout
.writeExternal(layoutElement
);
1488 public void setDefaultState(@NotNull final ToolWindowImpl toolWindow
,
1489 @Nullable final ToolWindowAnchor anchor
,
1490 @Nullable final ToolWindowType type
,
1491 @Nullable final Rectangle floatingBounds
) {
1493 final WindowInfoImpl info
= getInfo(toolWindow
.getId());
1494 if (info
.wasRead()) return;
1496 if (floatingBounds
!= null) {
1497 info
.setFloatingBounds(floatingBounds
);
1500 if (anchor
!= null) {
1501 toolWindow
.setAnchor(anchor
, null);
1505 toolWindow
.setType(type
, null);
1510 public void doWhenFocusSettlesDown(@NotNull final Runnable runnable
) {
1511 final boolean needsRestart
= isIdleQueueEmpty();
1512 myIdleRequests
.add(runnable
);
1518 private void restartIdleAlarm() {
1519 myIdleAlarm
.cancelAllRequests();
1520 myIdleAlarm
.addRequest(myIdleRunnable
, 20);
1523 private void flushIdleRequests() {
1525 myFlushingIdleRequestsEntryCount
++;
1527 final KeyEvent
[] events
= myToDispatchOnDone
.toArray(new KeyEvent
[myToDispatchOnDone
.size()]);
1528 IdeEventQueue
.getInstance().getKeyEventDispatcher().resetState();
1530 for (int i
= 0; i
< events
.length
; i
++) {
1531 KeyEvent each
= events
[i
];
1532 if (!isFocusTransferReady()) break;
1534 final Component owner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
1535 if (owner
!= null && SwingUtilities
.getWindowAncestor(owner
) != null) {
1536 myToDispatchOnDone
.remove(each
);
1537 IdeEventQueue
.getInstance().dispatchEvent(
1538 new KeyEvent(owner
, each
.getID(), each
.getWhen(), each
.getModifiersEx(), each
.getKeyCode(), each
.getKeyChar(),
1539 each
.getKeyLocation()));
1547 if (isPendingKeyEventsRedispatched()) {
1548 final Runnable
[] all
= myIdleRequests
.toArray(new Runnable
[myIdleRequests
.size()]);
1549 myIdleRequests
.clear();
1550 for (Runnable each
: all
) {
1556 myFlushingIdleRequestsEntryCount
--;
1557 if (!isIdleQueueEmpty()) {
1563 private String
toString(KeyEvent e
) {
1564 return KeyStroke
.getKeyStrokeForEvent(e
).toString();
1567 public boolean isFocusTransferReady() {
1568 return myFocusRequests
.isEmpty() && (myQueue
== null || !myQueue
.isSuspendMode());
1571 private boolean isIdleQueueEmpty() {
1572 return isPendingKeyEventsRedispatched() && myIdleRequests
.size() == 0;
1575 private boolean isPendingKeyEventsRedispatched() {
1576 return myToDispatchOnDone
.size() == 0;
1579 public boolean dispatch(KeyEvent e
) {
1580 if (!Registry
.is("actionSystem.fixLostTyping")) return false;
1582 if (myFlushingIdleRequestsEntryCount
> 0) return false;
1584 if (!isFocusTransferReady() || !isPendingKeyEventsRedispatched()) {
1585 for (FocusCommand each
: myFocusRequests
) {
1586 final KeyEventProcessor processor
= each
.getProcessor();
1587 if (processor
!= null) {
1588 final Boolean result
= processor
.dispatch(e
, myKeyProcessorContext
);
1589 if (result
!= null) {
1590 return result
.booleanValue();
1595 myToDispatchOnDone
.add(e
);
1605 public void suspendKeyProcessingUntil(final ActionCallback done
) {
1606 requestFocus(new FocusCommand(done
) {
1607 public ActionCallback
run() {
1610 }.saveAllocation(), true);
1614 * This command creates and shows <code>FloatingDecorator</code>.
1616 private final class AddFloatingDecoratorCmd
extends FinalizableCommand
{
1617 private final FloatingDecorator myFloatingDecorator
;
1620 * Creates floating decorator for specified floating decorator.
1622 public AddFloatingDecoratorCmd(final InternalDecorator decorator
, final WindowInfoImpl info
) {
1623 super(myWindowManager
.getCommandProcessor());
1624 myFloatingDecorator
= new FloatingDecorator(myFrame
, info
.copy(), decorator
);
1625 myId2FloatingDecorator
.put(info
.getId(), myFloatingDecorator
);
1626 final Rectangle bounds
= info
.getFloatingBounds();
1627 if (bounds
!= null &&
1629 bounds
.height
> 0 &&
1630 myWindowManager
.isInsideScreenBounds(bounds
.x
, bounds
.y
, bounds
.width
)) {
1631 myFloatingDecorator
.setBounds(bounds
);
1633 else { // place new frame at the center of main frame if there are no floating bounds
1634 Dimension size
= decorator
.getSize();
1635 if (size
.width
== 0 || size
.height
== 0) {
1636 size
= decorator
.getPreferredSize();
1638 myFloatingDecorator
.setSize(size
);
1639 myFloatingDecorator
.setLocationRelativeTo(myFrame
);
1645 myFloatingDecorator
.show();
1654 * This command hides and destroys floating decorator for tool window
1655 * with specified <code>ID</code>.
1657 private final class RemoveFloatingDecoratorCmd
extends FinalizableCommand
{
1658 private final FloatingDecorator myFloatingDecorator
;
1660 public RemoveFloatingDecoratorCmd(final WindowInfoImpl info
) {
1661 super(myWindowManager
.getCommandProcessor());
1662 myFloatingDecorator
= getFloatingDecorator(info
.getId());
1663 myId2FloatingDecorator
.remove(info
.getId());
1664 info
.setFloatingBounds(myFloatingDecorator
.getBounds());
1669 if (Patches
.SPECIAL_WINPUT_METHOD_PROCESSING
) {
1670 myFloatingDecorator
.remove(myFloatingDecorator
.getRootPane());
1672 myFloatingDecorator
.dispose();
1680 public Condition
getExpireCondition() {
1681 return Condition
.FALSE
;
1685 private final class EditorComponentFocusWatcher
extends FocusWatcher
{
1686 protected void focusedComponentChanged(final Component component
, final AWTEvent cause
) {
1687 if (myWindowManager
.getCommandProcessor().getCommandCount() > 0 || component
== null) {
1691 // Sometimes focus gained comes when editor is active. For example it can happen when
1692 // user switches between menus or closes some dialog. In that case we just ignore this event,
1693 // i.e. don't initiate deactivation of tool windows and requesting focus in editor.
1694 if (myEditorComponentActive
) {
1699 final KeyboardFocusManager mgr
= KeyboardFocusManager
.getCurrentKeyboardFocusManager();
1700 final Component owner
= mgr
.getFocusOwner();
1702 IdeFocusManager
.getInstance(myProject
).doWhenFocusSettlesDown(new Runnable() {
1704 if (mgr
.getFocusOwner() == owner
) {
1705 activateEditorComponent(false);
1714 * Notifies window manager about focus traversal in tool window
1716 private final class ToolWindowFocusWatcher
extends FocusWatcher
{
1717 private final String myId
;
1720 public ToolWindowFocusWatcher(final ToolWindowImpl toolWindow
) {
1721 myId
= toolWindow
.getId();
1722 install(toolWindow
.getComponent());
1725 protected boolean isFocusedComponentChangeValid(final Component comp
, final AWTEvent cause
) {
1726 return myWindowManager
.getCommandProcessor().getCommandCount() == 0 && comp
!= null;
1729 protected void focusedComponentChanged(final Component component
, final AWTEvent cause
) {
1730 if (myWindowManager
.getCommandProcessor().getCommandCount() > 0 || component
== null) {
1733 final WindowInfoImpl info
= getInfo(myId
);
1734 myFocusedComponentAlaram
.cancelAllRequests();
1736 if (!info
.isActive()) {
1737 myFocusedComponentAlaram
.addRequest(new EdtRunnable() {
1738 public void runEdt() {
1739 if (!myLayout
.isToolWindowRegistered(myId
)) return;
1740 activateToolWindow(myId
, false, false);
1748 * Spies on IdeToolWindow properties and applies them to the window
1751 private final class MyToolWindowPropertyChangeListener
implements PropertyChangeListener
{
1752 public void propertyChange(final PropertyChangeEvent e
) {
1753 final ToolWindowImpl toolWindow
= (ToolWindowImpl
)e
.getSource();
1754 if (ToolWindowEx
.PROP_AVAILABLE
.equals(e
.getPropertyName())) {
1755 final WindowInfoImpl info
= getInfo(toolWindow
.getId());
1756 if (!toolWindow
.isAvailable() && info
.isVisible()) {
1757 hideToolWindow(toolWindow
.getId(), false);
1764 * Translates events from InternalDecorator into ToolWindowManager method invocations.
1766 private final class MyInternalDecoratorListener
implements InternalDecoratorListener
{
1767 public void anchorChanged(final InternalDecorator source
, final ToolWindowAnchor anchor
) {
1768 setToolWindowAnchor(source
.getToolWindow().getId(), anchor
);
1771 public void autoHideChanged(final InternalDecorator source
, final boolean autoHide
) {
1772 setToolWindowAutoHide(source
.getToolWindow().getId(), autoHide
);
1775 public void hidden(final InternalDecorator source
) {
1776 hideToolWindow(source
.getToolWindow().getId(), false);
1779 public void hiddenSide(final InternalDecorator source
) {
1780 hideToolWindow(source
.getToolWindow().getId(), true);
1784 * Handles event from decorator and modify weight/floating bounds of the
1785 * tool window depending on decoration type.
1787 public void resized(final InternalDecorator source
) {
1788 final WindowInfoImpl info
= getInfo(source
.getToolWindow().getId());
1789 if (info
.isFloating()) {
1790 final Window owner
= SwingUtilities
.getWindowAncestor(source
);
1791 if (owner
!= null) {
1792 info
.setFloatingBounds(owner
.getBounds());
1795 else { // docked and sliding windows
1796 if (ToolWindowAnchor
.TOP
== info
.getAnchor() || ToolWindowAnchor
.BOTTOM
== info
.getAnchor()) {
1797 info
.setWeight((float)source
.getHeight() / (float)myToolWindowsPane
.getMyLayeredPane().getHeight());
1798 float newSideWeight
= (float)source
.getWidth() / (float)myToolWindowsPane
.getMyLayeredPane().getWidth();
1799 if (newSideWeight
< 1.0f
) {
1800 info
.setSideWeight(newSideWeight
);
1804 info
.setWeight((float)source
.getWidth() / (float)myToolWindowsPane
.getMyLayeredPane().getWidth());
1805 float newSideWeight
= (float)source
.getHeight() / (float)myToolWindowsPane
.getMyLayeredPane().getHeight();
1806 if (newSideWeight
< 1.0f
) {
1807 info
.setSideWeight(newSideWeight
);
1813 public void activated(final InternalDecorator source
) {
1814 activateToolWindow(source
.getToolWindow().getId(), true, true);
1817 public void typeChanged(final InternalDecorator source
, final ToolWindowType type
) {
1818 setToolWindowType(source
.getToolWindow().getId(), type
);
1821 public void sideStatusChanged(final InternalDecorator source
, final boolean isSideTool
) {
1822 setSideTool(source
.getToolWindow().getId(), isSideTool
);
1826 private void updateComponentTreeUI() {
1827 ApplicationManager
.getApplication().assertIsDispatchThread();
1828 final WindowInfoImpl
[] infos
= myLayout
.getInfos();
1829 for (final WindowInfoImpl info
: infos
) {
1830 if (info
.isVisible()) { // skip visible tool windows (optimization)
1833 SwingUtilities
.updateComponentTreeUI(getInternalDecorator(info
.getId()));
1837 private final class MyUIManagerPropertyChangeListener
implements PropertyChangeListener
{
1838 public void propertyChange(final PropertyChangeEvent e
) {
1839 updateComponentTreeUI();
1843 private final class MyLafManagerListener
implements LafManagerListener
{
1844 public void lookAndFeelChanged(final LafManager source
) {
1845 updateComponentTreeUI();
1850 public WindowManagerEx
getWindowManager() {
1851 return myWindowManager
;
1855 public String
getComponentName() {
1856 return "ToolWindowManager";
1860 public ToolWindowsPane
getToolWindowsPane() {
1861 return myToolWindowsPane
;
1864 public ActionCallback
requestFocus(final Component c
, final boolean forced
) {
1865 return requestFocus(new FocusCommand
.ByComponent(c
), forced
);
1868 public ActionCallback
requestFocus(final FocusCommand command
, final boolean forced
) {
1869 final ActionCallback result
= new ActionCallback();
1872 myUnforcedRequestFocusCmd
= command
;
1873 myFocusRequests
.add(command
);
1875 SwingUtilities
.invokeLater(new Runnable() {
1877 resetUnforcedCommand(command
);
1878 _requestFocus(command
, forced
, result
);
1883 _requestFocus(command
, forced
, result
);
1886 result
.doWhenProcessed(new Runnable() {
1895 private void _requestFocus(final FocusCommand command
, final boolean forced
, final ActionCallback result
) {
1896 if (checkForRejectOrByPass(command
, forced
, result
)) return;
1898 setCommand(command
);
1901 myForcedFocusRequestsAlarm
.cancelAllRequests();
1902 setLastEffectiveForcedRequest(command
);
1905 fixStickingDialogs();
1907 SwingUtilities
.invokeLater(new Runnable() {
1909 if (checkForRejectOrByPass(command
, forced
, result
)) return;
1911 if (myRequestFocusCmd
== command
) {
1912 final ActionCallback
.TimedOut focusTimeout
=
1913 new ActionCallback
.TimedOut(Registry
.intValue("actionSystem.commandProcessingTimeout"),
1914 "Focus command timed out, cmd=" + command
, command
.getAllocation(), true) {
1916 protected void onTimeout() {
1917 forceFinishFocusSettledown(command
, result
);
1921 command
.run().doWhenDone(new Runnable() {
1923 SwingUtilities
.invokeLater(new Runnable() {
1929 }).doWhenRejected(new Runnable() {
1931 result
.setRejected();
1933 }).doWhenProcessed(new Runnable() {
1935 resetCommand(command
);
1938 myForcedFocusRequestsAlarm
.addRequest(new EdtRunnable() {
1939 public void runEdt() {
1940 setLastEffectiveForcedRequest(null);
1945 }).notify(focusTimeout
);
1948 rejectCommand(command
, result
);
1954 private void forceFinishFocusSettledown(FocusCommand cmd
, ActionCallback cmdCallback
) {
1955 rejectCommand(cmd
, cmdCallback
);
1956 myUnforcedRequestFocusCmd
= null;
1959 private void fixStickingDialogs() {
1960 if (!Patches
.STICKY_DIALOGS
) return;
1962 final KeyboardFocusManager mgr
= KeyboardFocusManager
.getCurrentKeyboardFocusManager();
1963 final Window wnd
= mgr
.getActiveWindow();
1964 if (wnd
!= null && !wnd
.isShowing() && wnd
.getParent() instanceof Window
) {
1965 final Container parent
= wnd
.getParent();
1966 final Method setActive
=
1967 ReflectionUtil
.findMethod(KeyboardFocusManager
.class.getDeclaredMethods(), "setGlobalActiveWindow", Window
.class);
1968 if (setActive
!= null) {
1970 setActive
.setAccessible(true);
1971 setActive
.invoke(mgr
, (Window
)parent
);
1973 catch (Exception e
) {
1980 private boolean checkForRejectOrByPass(final FocusCommand cmd
, final boolean forced
, final ActionCallback result
) {
1981 if (cmd
.isExpired()) {
1982 rejectCommand(cmd
, result
);
1986 final FocusCommand lastRequest
= getLastEffectiveForcedRequest();
1988 if (!forced
&& !isUnforcedRequestAllowed()) {
1989 if (cmd
.equals(lastRequest
)) {
1993 rejectCommand(cmd
, result
);
1999 if (lastRequest
!= null && lastRequest
.dominatesOver(cmd
)) {
2000 rejectCommand(cmd
, result
);
2004 if (!myApp
.isActive() && !canExecuteOnInactiveApplication(cmd
)) {
2005 if (myCallbackOnActivation
!= null) {
2006 myCallbackOnActivation
.setRejected();
2009 myFocusCommandOnAppActivation
= cmd
;
2010 myCallbackOnActivation
= result
;
2018 private void rejectCommand(FocusCommand cmd
, ActionCallback callback
) {
2020 resetUnforcedCommand(cmd
);
2022 callback
.setRejected();
2025 private void setCommand(FocusCommand command
) {
2026 myRequestFocusCmd
= command
;
2028 if (!myFocusRequests
.contains(command
)) {
2029 myFocusRequests
.add(command
);
2033 private void resetCommand(FocusCommand cmd
) {
2034 if (cmd
== myRequestFocusCmd
) {
2035 myRequestFocusCmd
= null;
2038 final KeyEventProcessor processor
= cmd
.getProcessor();
2039 if (processor
!= null) {
2040 processor
.finish(myKeyProcessorContext
);
2043 myFocusRequests
.remove(cmd
);
2046 private void resetUnforcedCommand(FocusCommand cmd
) {
2047 myUnforcedRequestFocusCmd
= null;
2048 myFocusRequests
.remove(cmd
);
2051 private boolean canExecuteOnInactiveApplication(FocusCommand cmd
) {
2052 return !Patches
.REQUEST_FOCUS_MAY_ACTIVATE_APP
|| cmd
.canExecuteOnInactiveApp();
2055 private void setLastEffectiveForcedRequest(FocusCommand command
) {
2056 myLastForcedRequest
= new WeakReference
<FocusCommand
>(command
);
2060 private FocusCommand
getLastEffectiveForcedRequest() {
2061 if (myLastForcedRequest
== null) return null;
2062 final FocusCommand request
= myLastForcedRequest
.get();
2063 return request
!= null && !request
.isExpired() ? request
: null;
2066 private boolean isUnforcedRequestAllowed() {
2067 return getLastEffectiveForcedRequest() == null;
2070 private boolean isProjectComponent(Component c
) {
2071 final Component frame
= UIUtil
.findUltimateParent(c
);
2072 if (frame
instanceof IdeFrame
) {
2073 return frame
== myWindowManager
.getFrame(myProject
);
2080 private class AppListener
extends ApplicationAdapter
{
2083 public void applicationDeactivated(IdeFrame ideFrame
) {
2084 Component c
= getLastFocusedProjectComponent();
2086 final Component owner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
2087 if (isProjectComponent(owner
)) {
2091 myFocusedComponentOnDeactivation
= c
!= null ?
new WeakReference
<Component
>(c
) : null;
2095 public void applicationActivated(IdeFrame ideFrame
) {
2096 final FocusCommand cmd
= myFocusCommandOnAppActivation
;
2097 ActionCallback callback
= myCallbackOnActivation
;
2098 myFocusCommandOnAppActivation
= null;
2099 myCallbackOnActivation
= null;
2100 if (cmd
!= null && !cmd
.isExpired()) {
2101 requestFocus(cmd
, true).notifyWhenDone(callback
);
2104 final KeyboardFocusManager mgr
= KeyboardFocusManager
.getCurrentKeyboardFocusManager();
2106 if (ideFrame
== myWindowManager
.getFrame(myProject
)) {
2107 final Component owner
= mgr
.getFocusOwner();
2108 Component old
= myFocusedComponentOnDeactivation
!= null ? myFocusedComponentOnDeactivation
.get() : null;
2110 if (old
== null || !old
.isShowing()) {
2111 old
= IdeFocusTraversalPolicy
.getPreferredFocusedComponent(((IdeFrameImpl
)ideFrame
).getRootPane());
2114 if (owner
== null && old
!= null && old
.isShowing()) {
2115 requestFocus(old
, false);
2117 myFocusedComponentOnDeactivation
= null;
2123 private static class EdtAlarm
{
2124 private Alarm myAlarm
;
2126 public EdtAlarm(Disposable parent
) {
2127 myAlarm
= new Alarm(Alarm
.ThreadToUse
.OWN_THREAD
, parent
);
2130 public int getActiveRequestCount() {
2131 return myAlarm
.getActiveRequestCount();
2134 public void cancelAllRequests() {
2135 myAlarm
.cancelAllRequests();
2138 public void addRequest(EdtRunnable runnable
, int delay
) {
2139 myAlarm
.addRequest(runnable
, delay
);
2143 private class KeyProcessorConext
implements KeyEventProcessor
.Context
{
2144 public List
<KeyEvent
> getQueue() {
2145 return myToDispatchOnDone
;
2148 public void dispatch(final List
<KeyEvent
> events
) {
2149 doWhenFocusSettlesDown(new Runnable() {
2151 myToDispatchOnDone
.addAll(events
);