workaround for sticky focused components and windows
[fedora-idea.git] / platform-impl / src / com / intellij / openapi / wm / impl / ToolWindowManagerImpl.java
blob457cdba47beaacf75431e116376d16774db4d99e
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;
43 import javax.swing.*;
44 import javax.swing.event.EventListenerList;
45 import javax.swing.event.HyperlinkListener;
46 import java.awt.*;
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;
52 import java.util.*;
53 import java.util.List;
55 /**
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()) {
102 flushIdleRequests();
104 else {
105 restartIdleAlarm();
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();
135 myProject = project;
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);
160 myApp = app;
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);
178 return false;
180 }, myProject);
183 private Component getLastFocusedProjectComponent() {
184 return myLastFocusedProjectComponent != null ? myLastFocusedProjectComponent.get() : null;
187 public Project getProject() {
188 return myProject;
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)) {
232 hiddenIds.add(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) {
245 showToolWindow(id);
247 hiddenIds.clear();
250 myProject.getMessageBus().connect().subscribe(DumbService.DUMB_MODE, dumbModeListener);
252 StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() {
253 public void run() {
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)) {
267 continue;
269 ToolWindowAnchor toolWindowAnchor;
270 try {
271 toolWindowAnchor = ToolWindowAnchor.fromText(bean.anchor);
273 catch (Exception e) {
274 LOG.error(e);
275 continue;
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());
282 if (icon == null) {
283 try {
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() {
291 public void run() {
292 ApplicationManager.getApplication().invokeLater(new Runnable() {
293 public void run() {
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) {
342 fireStateChanged();
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() {
367 public void run() {
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() {
380 public void run() {
381 if (forced) {
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);
389 execute(cmds);
391 if (reactivateLastActive) {
392 activateToolWindow(active, false, true);
394 else {
395 if (active != null) {
396 myActiveStack.remove(active, false);
399 if (!myActiveStack.isEmpty()) {
400 activateToolWindow(myActiveStack.peek(), false, true);
403 return new ActionCallback.Done();
405 }, false);
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())) {
416 continue;
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()) {
440 return;
442 // show activated
443 final WindowInfoImpl info = getInfo(id);
444 boolean toApplyInfo = false;
445 if (!info.isActive()) {
446 info.setActive(true);
447 toApplyInfo = true;
449 showToolWindowImpl(id, dirtyMode, commandsList);
451 // activate
452 if (toApplyInfo) {
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();
468 checkId(id);
469 if (DumbService.getInstance(myProject).isDumb() && !myDumbAwareIds.contains(id)) {
470 return;
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,
480 boolean forced,
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);
495 return;
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();
544 return ids;
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;
566 break;
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)) {
608 return null;
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();
625 checkId(id);
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);
656 else {
658 // first of all we have to find tool window that was located at the same side and
659 // was hidden.
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()) {
671 info2 = storedInfo;
672 break;
675 if (info2 != null) {
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
685 if (wasActive) {
686 if (myActiveStack.isEmpty()) {
687 activateEditorComponentImpl(commandList, false);
689 else {
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()) {
707 return;
710 if (DumbService.getInstance(myProject).isDumb() && !myDumbAwareIds.contains(id)) {
711 return;
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())) {
732 continue;
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,
812 boolean sideTool,
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);
829 // Create decorator
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())) {
857 if (wasActive) {
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);
870 return toolWindow;
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);
879 return window;
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)) {
888 return;
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);
915 button.dispose();
916 myId2StripeButton.remove(id);
918 myId2FocusWatcher.remove(id);
919 // Destroy decorator
920 final InternalDecorator decorator = getInternalDecorator(id);
921 decorator.dispose();
922 decorator.removeInternalDecoratorListener(myInternalDecoratorListener);
923 myId2InternalDecorator.remove(id);
926 public DesktopLayout getLayout() {
927 ApplicationManager.getApplication().assertIsDispatchThread();
928 return myLayout;
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);
946 if (info == null) {
947 continue;
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);
956 if (info == null) {
957 continue;
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);
966 if (info == null) {
967 continue;
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);
976 if (info == null) {
977 continue;
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);
986 if (info == null) {
987 continue;
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);
1011 @Override
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();
1029 stripe.repaint();
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)
1051 .createBalloon();
1052 Disposer.register(balloon, new Disposable() {
1053 public void dispose() {
1054 window.setPlaceholderMode(false);
1055 stripe.updateState();
1056 stripe.revalidate();
1057 stripe.repaint();
1061 final StripeButton button = stripe.getButtonFor(toolWindowId);
1062 if (button == null) return;
1064 final Runnable show = new Runnable() {
1065 public void run() {
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());
1070 else {
1071 final Rectangle bounds = myToolWindowsPane.getBounds();
1072 final Point target = UIUtil.getCenterPoint(bounds, new Dimension(1, 1));
1073 if (ToolWindowAnchor.TOP == anchor) {
1074 target.y = 0;
1076 else if (ToolWindowAnchor.BOTTOM == anchor) {
1077 target.y = bounds.height;
1079 else if (ToolWindowAnchor.LEFT == anchor) {
1080 target.x = 0;
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() {
1093 public void run() {
1094 show.run();
1098 else {
1099 show.run();
1104 public boolean isEditorComponentActive() {
1105 ApplicationManager.getApplication().assertIsDispatchThread();
1106 return myEditorComponentActive;
1109 ToolWindowAnchor getToolWindowAnchor(final String id) {
1110 ApplicationManager.getApplication().assertIsDispatchThread();
1111 checkId(id);
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,
1129 final int order,
1130 final ArrayList<FinalizableCommand> commandsList) {
1131 checkId(id);
1132 final WindowInfoImpl info = getInfo(id);
1133 if (anchor == info.getAnchor() && order == info.getOrder()) {
1134 return;
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
1141 // setAnchor method
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
1154 // setAnchor method
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();
1169 checkId(id);
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) {
1187 checkId(id);
1188 final WindowInfoImpl info = getInfo(id);
1189 if (isSplit == info.isSplit()) {
1190 return;
1193 myLayout.setSplitMode(id, isSplit);
1195 boolean wasActive = info.isActive();
1196 if (wasActive) {
1197 deactivateToolWindowImpl(id, true, commandList);
1199 final WindowInfoImpl[] infos = myLayout.getInfos();
1200 for (WindowInfoImpl info1 : infos) {
1201 appendApplyWindowInfoCmd(info1, commandList);
1203 if (wasActive) {
1204 activateToolWindowImpl(id, commandList, true, true);
1206 commandList.add(myToolWindowsPane.createUpdateButtonPositionCmd(id, myWindowManager.getCommandProcessor()));
1209 ToolWindowType getToolWindowInternalType(final String id) {
1210 ApplicationManager.getApplication().assertIsDispatchThread();
1211 checkId(id);
1212 return getInfo(id).getInternalType();
1215 ToolWindowType getToolWindowType(final String id) {
1216 ApplicationManager.getApplication().assertIsDispatchThread();
1217 checkId(id);
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();
1237 checkId(id);
1238 return getInfo(id).isActive();
1241 boolean isToolWindowAutoHide(final String id) {
1242 ApplicationManager.getApplication().assertIsDispatchThread();
1243 checkId(id);
1244 return getInfo(id).isAutoHide();
1247 public boolean isToolWindowFloating(final String id) {
1248 ApplicationManager.getApplication().assertIsDispatchThread();
1249 checkId(id);
1250 return getInfo(id).isFloating();
1253 boolean isToolWindowVisible(final String id) {
1254 ApplicationManager.getApplication().assertIsDispatchThread();
1255 checkId(id);
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) {
1267 checkId(id);
1268 final WindowInfoImpl info = getInfo(id);
1269 if (info.isAutoHide() == autoHide) {
1270 return;
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) {
1288 checkId(id);
1289 final WindowInfoImpl info = getInfo(id);
1290 if (info.getType() == type) {
1291 return;
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);
1302 info.setType(type);
1303 appendApplyWindowInfoCmd(info, commandsList);
1304 prepareForActivation(id, commandsList);
1305 showAndActivate(id, dirtyMode, commandsList, true);
1306 appendUpdateToolWindowsPaneCmd(commandsList);
1308 else {
1309 info.setType(type);
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()) {
1400 return false;
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()) {
1411 return true;
1413 final Window[] ownedWindows = dialog.getOwnedWindows();
1414 for (int i = ownedWindows.length - 1; i >= 0; i--) {
1415 if (isModalOrHasModalChild(ownedWindows[i])) {
1416 return true;
1420 return false;
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())) {
1432 continue;
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
1462 return;
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);
1511 if (type != null) {
1512 toolWindow.setType(type, null);
1517 public void doWhenFocusSettlesDown(@NotNull final Runnable runnable) {
1518 final boolean needsRestart = isIdleQueueEmpty();
1519 myIdleRequests.add(runnable);
1520 if (needsRestart) {
1521 restartIdleAlarm();
1525 private void restartIdleAlarm() {
1526 myIdleAlarm.cancelAllRequests();
1527 myIdleAlarm.addRequest(myIdleRunnable, 20);
1530 private void flushIdleRequests() {
1531 try {
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()));
1548 else {
1549 break;
1554 if (isPendingKeyEventsRedispatched()) {
1555 final Runnable[] all = myIdleRequests.toArray(new Runnable[myIdleRequests.size()]);
1556 myIdleRequests.clear();
1557 for (Runnable each : all) {
1558 each.run();
1562 finally {
1563 myFlushingIdleRequestsEntryCount--;
1564 if (!isIdleQueueEmpty()) {
1565 restartIdleAlarm();
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);
1603 restartIdleAlarm();
1605 return true;
1607 else {
1608 return false;
1612 public void suspendKeyProcessingUntil(final ActionCallback done) {
1613 requestFocus(new FocusCommand(done) {
1614 public ActionCallback run() {
1615 return done;
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 &&
1636 bounds.width > 0 &&
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);
1651 public void run() {
1652 try {
1653 myFloatingDecorator.show();
1655 finally {
1656 finish();
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());
1675 public void run() {
1676 try {
1677 if (Patches.SPECIAL_WINPUT_METHOD_PROCESSING) {
1678 myFloatingDecorator.remove(myFloatingDecorator.getRootPane());
1680 myFloatingDecorator.dispose();
1682 finally {
1683 finish();
1687 @Nullable
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) {
1696 return;
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) {
1703 return;
1707 final KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
1708 final Component owner = mgr.getFocusOwner();
1710 IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new Runnable() {
1711 public void run() {
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) {
1739 return;
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);
1750 }, 100);
1756 * Spies on IdeToolWindow properties and applies them to the window
1757 * state.
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);
1811 else {
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)
1839 continue;
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;
1862 @NotNull
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);
1881 }, forced);
1884 private ActionCallback processDefaultFocusRequest(boolean forced) {
1885 if (ModalityState.NON_MODAL.equals(ModalityState.current())) {
1886 if (myEditorComponentActive) {
1887 activateEditorComponent(forced);
1888 } else {
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();
1898 if (!forced) {
1899 myUnforcedRequestFocusCmd = command;
1900 myFocusRequests.add(command);
1902 SwingUtilities.invokeLater(new Runnable() {
1903 public void run() {
1904 resetUnforcedCommand(command);
1905 _requestFocus(command, forced, result);
1909 else {
1910 _requestFocus(command, forced, result);
1913 result.doWhenProcessed(new Runnable() {
1914 public void run() {
1915 restartIdleAlarm();
1919 return result;
1922 private void _requestFocus(final FocusCommand command, final boolean forced, final ActionCallback result) {
1923 if (checkForRejectOrByPass(command, forced, result)) return;
1925 setCommand(command);
1927 if (forced) {
1928 myForcedFocusRequestsAlarm.cancelAllRequests();
1929 setLastEffectiveForcedRequest(command);
1932 SwingUtilities.invokeLater(new Runnable() {
1933 public void run() {
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) {
1940 @Override
1941 protected void onTimeout() {
1942 forceFinishFocusSettledown(command, result);
1946 command.run().doWhenDone(new Runnable() {
1947 public void run() {
1948 SwingUtilities.invokeLater(new Runnable() {
1949 public void run() {
1950 result.setDone();
1954 }).doWhenRejected(new Runnable() {
1955 public void run() {
1956 result.setRejected();
1958 }).doWhenProcessed(new Runnable() {
1959 public void run() {
1960 resetCommand(command);
1962 if (forced) {
1963 myForcedFocusRequestsAlarm.addRequest(new EdtRunnable() {
1964 public void runEdt() {
1965 setLastEffectiveForcedRequest(null);
1967 }, 250);
1970 }).notify(focusTimeout);
1972 else {
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);
1987 return true;
1990 final FocusCommand lastRequest = getLastEffectiveForcedRequest();
1992 if (!forced && !isUnforcedRequestAllowed()) {
1993 if (cmd.equals(lastRequest)) {
1994 result.setDone();
1996 else {
1997 rejectCommand(cmd, result);
1999 return true;
2003 if (lastRequest != null && lastRequest.dominatesOver(cmd)) {
2004 rejectCommand(cmd, result);
2005 return true;
2008 if (!myApp.isActive() && !canExecuteOnInactiveApplication(cmd)) {
2009 if (myCallbackOnActivation != null) {
2010 myCallbackOnActivation.setRejected();
2013 myFocusCommandOnAppActivation = cmd;
2014 myCallbackOnActivation = result;
2016 return true;
2019 return false;
2022 private void rejectCommand(FocusCommand cmd, ActionCallback callback) {
2023 resetCommand(cmd);
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);
2063 @Nullable
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);
2079 else {
2080 return false;
2084 private class AppListener extends ApplicationAdapter {
2086 @Override
2087 public void applicationDeactivated(IdeFrame ideFrame) {
2088 Component c;
2089 final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
2090 if (isProjectComponent(owner)) {
2091 c = owner;
2092 } else {
2093 c = getLastFocusedProjectComponent();
2096 myFocusedComponentOnDeactivation = c != null ? new WeakReference<Component>(c) : null;
2099 @Override
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);
2108 else {
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() {
2151 public void run() {
2152 myToDispatchOnDone.addAll(events);
2153 restartIdleAlarm();