problem with icon loading from plugin resources
[fedora-idea.git] / platform-impl / src / com / intellij / openapi / wm / impl / ToolWindowManagerImpl.java
blob301014b2575bd34a4f2c34aa7236fe5feda69a17
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;
42 import javax.swing.*;
43 import javax.swing.event.EventListenerList;
44 import javax.swing.event.HyperlinkListener;
45 import java.awt.*;
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;
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);
177 return false;
179 }, myProject);
182 private Component getLastFocusedProjectComponent() {
183 return myLastFocusedProjectComponent != null ? myLastFocusedProjectComponent.get() : null;
186 public Project getProject() {
187 return myProject;
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)) {
231 hiddenIds.add(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) {
244 showToolWindow(id);
246 hiddenIds.clear();
249 myProject.getMessageBus().connect().subscribe(DumbService.DUMB_MODE, dumbModeListener);
251 StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() {
252 public void run() {
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)) {
266 continue;
268 ToolWindowAnchor toolWindowAnchor;
269 try {
270 toolWindowAnchor = ToolWindowAnchor.fromText(bean.anchor);
272 catch (Exception e) {
273 LOG.error(e);
274 continue;
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());
281 if (icon == null) {
282 try {
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() {
290 public void run() {
291 ApplicationManager.getApplication().invokeLater(new Runnable() {
292 public void run() {
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) {
341 fireStateChanged();
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() {
366 public void run() {
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() {
379 public void run() {
380 if (forced) {
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);
388 execute(cmds);
390 if (reactivateLastActive) {
391 activateToolWindow(active, false, true);
393 else {
394 if (active != null) {
395 myActiveStack.remove(active, false);
398 if (!myActiveStack.isEmpty()) {
399 activateToolWindow(myActiveStack.peek(), false, true);
402 return new ActionCallback.Done();
404 }, false);
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())) {
415 continue;
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()) {
439 return;
441 // show activated
442 final WindowInfoImpl info = getInfo(id);
443 boolean toApplyInfo = false;
444 if (!info.isActive()) {
445 info.setActive(true);
446 toApplyInfo = true;
448 showToolWindowImpl(id, dirtyMode, commandsList);
450 // activate
451 if (toApplyInfo) {
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();
467 checkId(id);
468 if (DumbService.getInstance().isDumb() && !myDumbAwareIds.contains(id)) {
469 return;
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,
479 boolean forced,
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);
494 return;
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();
543 return ids;
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;
565 break;
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)) {
607 return null;
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();
624 checkId(id);
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);
655 else {
657 // first of all we have to find tool window that was located at the same side and
658 // was hidden.
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()) {
670 info2 = storedInfo;
671 break;
674 if (info2 != null) {
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
684 if (wasActive) {
685 if (myActiveStack.isEmpty()) {
686 activateEditorComponentImpl(commandList, false);
688 else {
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()) {
706 return;
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())) {
727 continue;
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,
807 boolean sideTool,
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);
824 // Create decorator
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())) {
850 if (wasActive) {
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);
863 return toolWindow;
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);
872 return window;
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)) {
881 return;
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);
908 button.dispose();
909 myId2StripeButton.remove(id);
911 myId2FocusWatcher.remove(id);
912 // Destroy decorator
913 final InternalDecorator decorator = getInternalDecorator(id);
914 decorator.dispose();
915 decorator.removeInternalDecoratorListener(myInternalDecoratorListener);
916 myId2InternalDecorator.remove(id);
919 public DesktopLayout getLayout() {
920 ApplicationManager.getApplication().assertIsDispatchThread();
921 return myLayout;
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);
939 if (info == null) {
940 continue;
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);
949 if (info == null) {
950 continue;
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);
959 if (info == null) {
960 continue;
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);
969 if (info == null) {
970 continue;
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);
979 if (info == null) {
980 continue;
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);
1004 @Override
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();
1022 stripe.repaint();
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)
1044 .createBalloon();
1045 Disposer.register(balloon, new Disposable() {
1046 public void dispose() {
1047 window.setPlaceholderMode(false);
1048 stripe.updateState();
1049 stripe.revalidate();
1050 stripe.repaint();
1054 final StripeButton button = stripe.getButtonFor(toolWindowId);
1055 if (button == null) return;
1057 final Runnable show = new Runnable() {
1058 public void run() {
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());
1063 else {
1064 final Rectangle bounds = myToolWindowsPane.getBounds();
1065 final Point target = UIUtil.getCenterPoint(bounds, new Dimension(1, 1));
1066 if (ToolWindowAnchor.TOP == anchor) {
1067 target.y = 0;
1069 else if (ToolWindowAnchor.BOTTOM == anchor) {
1070 target.y = bounds.height;
1072 else if (ToolWindowAnchor.LEFT == anchor) {
1073 target.x = 0;
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() {
1086 public void run() {
1087 show.run();
1091 else {
1092 show.run();
1097 public boolean isEditorComponentActive() {
1098 ApplicationManager.getApplication().assertIsDispatchThread();
1099 return myEditorComponentActive;
1102 ToolWindowAnchor getToolWindowAnchor(final String id) {
1103 ApplicationManager.getApplication().assertIsDispatchThread();
1104 checkId(id);
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,
1122 final int order,
1123 final ArrayList<FinalizableCommand> commandsList) {
1124 checkId(id);
1125 final WindowInfoImpl info = getInfo(id);
1126 if (anchor == info.getAnchor() && order == info.getOrder()) {
1127 return;
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
1134 // setAnchor method
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
1147 // setAnchor method
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();
1162 checkId(id);
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) {
1180 checkId(id);
1181 final WindowInfoImpl info = getInfo(id);
1182 if (isSplit == info.isSplit()) {
1183 return;
1186 myLayout.setSplitMode(id, isSplit);
1188 boolean wasActive = info.isActive();
1189 if (wasActive) {
1190 deactivateToolWindowImpl(id, true, commandList);
1192 final WindowInfoImpl[] infos = myLayout.getInfos();
1193 for (WindowInfoImpl info1 : infos) {
1194 appendApplyWindowInfoCmd(info1, commandList);
1196 if (wasActive) {
1197 activateToolWindowImpl(id, commandList, true, true);
1199 commandList.add(myToolWindowsPane.createUpdateButtonPositionCmd(id, myWindowManager.getCommandProcessor()));
1202 ToolWindowType getToolWindowInternalType(final String id) {
1203 ApplicationManager.getApplication().assertIsDispatchThread();
1204 checkId(id);
1205 return getInfo(id).getInternalType();
1208 ToolWindowType getToolWindowType(final String id) {
1209 ApplicationManager.getApplication().assertIsDispatchThread();
1210 checkId(id);
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();
1230 checkId(id);
1231 return getInfo(id).isActive();
1234 boolean isToolWindowAutoHide(final String id) {
1235 ApplicationManager.getApplication().assertIsDispatchThread();
1236 checkId(id);
1237 return getInfo(id).isAutoHide();
1240 public boolean isToolWindowFloating(final String id) {
1241 ApplicationManager.getApplication().assertIsDispatchThread();
1242 checkId(id);
1243 return getInfo(id).isFloating();
1246 boolean isToolWindowVisible(final String id) {
1247 ApplicationManager.getApplication().assertIsDispatchThread();
1248 checkId(id);
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) {
1260 checkId(id);
1261 final WindowInfoImpl info = getInfo(id);
1262 if (info.isAutoHide() == autoHide) {
1263 return;
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) {
1281 checkId(id);
1282 final WindowInfoImpl info = getInfo(id);
1283 if (info.getType() == type) {
1284 return;
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);
1295 info.setType(type);
1296 appendApplyWindowInfoCmd(info, commandsList);
1297 prepareForActivation(id, commandsList);
1298 showAndActivate(id, dirtyMode, commandsList, true);
1299 appendUpdateToolWindowsPaneCmd(commandsList);
1301 else {
1302 info.setType(type);
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()) {
1393 return false;
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()) {
1404 return true;
1406 final Window[] ownedWindows = dialog.getOwnedWindows();
1407 for (int i = ownedWindows.length - 1; i >= 0; i--) {
1408 if (isModalOrHasModalChild(ownedWindows[i])) {
1409 return true;
1413 return false;
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())) {
1425 continue;
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
1455 return;
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);
1504 if (type != null) {
1505 toolWindow.setType(type, null);
1510 public void doWhenFocusSettlesDown(@NotNull final Runnable runnable) {
1511 final boolean needsRestart = isIdleQueueEmpty();
1512 myIdleRequests.add(runnable);
1513 if (needsRestart) {
1514 restartIdleAlarm();
1518 private void restartIdleAlarm() {
1519 myIdleAlarm.cancelAllRequests();
1520 myIdleAlarm.addRequest(myIdleRunnable, 20);
1523 private void flushIdleRequests() {
1524 try {
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()));
1541 else {
1542 break;
1547 if (isPendingKeyEventsRedispatched()) {
1548 final Runnable[] all = myIdleRequests.toArray(new Runnable[myIdleRequests.size()]);
1549 myIdleRequests.clear();
1550 for (Runnable each : all) {
1551 each.run();
1555 finally {
1556 myFlushingIdleRequestsEntryCount--;
1557 if (!isIdleQueueEmpty()) {
1558 restartIdleAlarm();
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);
1596 restartIdleAlarm();
1598 return true;
1600 else {
1601 return false;
1605 public void suspendKeyProcessingUntil(final ActionCallback done) {
1606 requestFocus(new FocusCommand(done) {
1607 public ActionCallback run() {
1608 return done;
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 &&
1628 bounds.width > 0 &&
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);
1643 public void run() {
1644 try {
1645 myFloatingDecorator.show();
1647 finally {
1648 finish();
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());
1667 public void run() {
1668 try {
1669 if (Patches.SPECIAL_WINPUT_METHOD_PROCESSING) {
1670 myFloatingDecorator.remove(myFloatingDecorator.getRootPane());
1672 myFloatingDecorator.dispose();
1674 finally {
1675 finish();
1679 @Nullable
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) {
1688 return;
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) {
1695 return;
1699 final KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
1700 final Component owner = mgr.getFocusOwner();
1702 IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new Runnable() {
1703 public void run() {
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) {
1731 return;
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);
1742 }, 100);
1748 * Spies on IdeToolWindow properties and applies them to the window
1749 * state.
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);
1803 else {
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)
1831 continue;
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;
1854 @NotNull
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();
1871 if (!forced) {
1872 myUnforcedRequestFocusCmd = command;
1873 myFocusRequests.add(command);
1875 SwingUtilities.invokeLater(new Runnable() {
1876 public void run() {
1877 resetUnforcedCommand(command);
1878 _requestFocus(command, forced, result);
1882 else {
1883 _requestFocus(command, forced, result);
1886 result.doWhenProcessed(new Runnable() {
1887 public void run() {
1888 restartIdleAlarm();
1892 return result;
1895 private void _requestFocus(final FocusCommand command, final boolean forced, final ActionCallback result) {
1896 if (checkForRejectOrByPass(command, forced, result)) return;
1898 setCommand(command);
1900 if (forced) {
1901 myForcedFocusRequestsAlarm.cancelAllRequests();
1902 setLastEffectiveForcedRequest(command);
1905 fixStickingDialogs();
1907 SwingUtilities.invokeLater(new Runnable() {
1908 public void run() {
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) {
1915 @Override
1916 protected void onTimeout() {
1917 forceFinishFocusSettledown(command, result);
1921 command.run().doWhenDone(new Runnable() {
1922 public void run() {
1923 SwingUtilities.invokeLater(new Runnable() {
1924 public void run() {
1925 result.setDone();
1929 }).doWhenRejected(new Runnable() {
1930 public void run() {
1931 result.setRejected();
1933 }).doWhenProcessed(new Runnable() {
1934 public void run() {
1935 resetCommand(command);
1937 if (forced) {
1938 myForcedFocusRequestsAlarm.addRequest(new EdtRunnable() {
1939 public void runEdt() {
1940 setLastEffectiveForcedRequest(null);
1942 }, 250);
1945 }).notify(focusTimeout);
1947 else {
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) {
1969 try {
1970 setActive.setAccessible(true);
1971 setActive.invoke(mgr, (Window)parent);
1973 catch (Exception e) {
1974 LOG.info(e);
1980 private boolean checkForRejectOrByPass(final FocusCommand cmd, final boolean forced, final ActionCallback result) {
1981 if (cmd.isExpired()) {
1982 rejectCommand(cmd, result);
1983 return true;
1986 final FocusCommand lastRequest = getLastEffectiveForcedRequest();
1988 if (!forced && !isUnforcedRequestAllowed()) {
1989 if (cmd.equals(lastRequest)) {
1990 result.setDone();
1992 else {
1993 rejectCommand(cmd, result);
1995 return true;
1999 if (lastRequest != null && lastRequest.dominatesOver(cmd)) {
2000 rejectCommand(cmd, result);
2001 return true;
2004 if (!myApp.isActive() && !canExecuteOnInactiveApplication(cmd)) {
2005 if (myCallbackOnActivation != null) {
2006 myCallbackOnActivation.setRejected();
2009 myFocusCommandOnAppActivation = cmd;
2010 myCallbackOnActivation = result;
2012 return true;
2015 return false;
2018 private void rejectCommand(FocusCommand cmd, ActionCallback callback) {
2019 resetCommand(cmd);
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);
2059 @Nullable
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);
2075 else {
2076 return false;
2080 private class AppListener extends ApplicationAdapter {
2082 @Override
2083 public void applicationDeactivated(IdeFrame ideFrame) {
2084 Component c = getLastFocusedProjectComponent();
2085 if (c == null) {
2086 final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
2087 if (isProjectComponent(owner)) {
2088 c = owner;
2091 myFocusedComponentOnDeactivation = c != null ? new WeakReference<Component>(c) : null;
2094 @Override
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);
2103 else {
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() {
2150 public void run() {
2151 myToDispatchOnDone.addAll(events);
2152 restartIdleAlarm();