847d78f3874bcc1979f52597ce41ed8ac14c013d
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / ToolWindowManagerImpl.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.wm.impl;
17
18 import com.intellij.ide.FrameStateManager;
19 import com.intellij.ide.IdeEventQueue;
20 import com.intellij.ide.actions.ActivateToolWindowAction;
21 import com.intellij.ide.ui.LafManager;
22 import com.intellij.ide.ui.LafManagerListener;
23 import com.intellij.openapi.Disposable;
24 import com.intellij.openapi.actionSystem.*;
25 import com.intellij.openapi.actionSystem.ex.AnActionListener;
26 import com.intellij.openapi.application.ApplicationManager;
27 import com.intellij.openapi.application.ModalityState;
28 import com.intellij.openapi.components.ProjectComponent;
29 import com.intellij.openapi.diagnostic.Logger;
30 import com.intellij.openapi.extensions.Extensions;
31 import com.intellij.openapi.fileEditor.FileEditorManager;
32 import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
33 import com.intellij.openapi.fileEditor.FileEditorManagerListener;
34 import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
35 import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
36 import com.intellij.openapi.keymap.Keymap;
37 import com.intellij.openapi.keymap.KeymapManager;
38 import com.intellij.openapi.project.DumbAwareRunnable;
39 import com.intellij.openapi.project.DumbService;
40 import com.intellij.openapi.project.Project;
41 import com.intellij.openapi.startup.StartupManager;
42 import com.intellij.openapi.ui.DialogWrapper;
43 import com.intellij.openapi.ui.MessageType;
44 import com.intellij.openapi.ui.Splitter;
45 import com.intellij.openapi.ui.popup.Balloon;
46 import com.intellij.openapi.ui.popup.JBPopupFactory;
47 import com.intellij.openapi.util.*;
48 import com.intellij.openapi.util.registry.Registry;
49 import com.intellij.openapi.vfs.VirtualFile;
50 import com.intellij.openapi.wm.*;
51 import com.intellij.openapi.wm.ex.*;
52 import com.intellij.openapi.wm.impl.commands.*;
53 import com.intellij.ui.BalloonImpl;
54 import com.intellij.ui.ColorUtil;
55 import com.intellij.ui.awt.RelativePoint;
56 import com.intellij.ui.switcher.QuickAccessSettings;
57 import com.intellij.ui.switcher.SwitchManager;
58 import com.intellij.util.*;
59 import com.intellij.util.containers.ContainerUtil;
60 import com.intellij.util.containers.HashMap;
61 import com.intellij.util.ui.PositionTracker;
62 import com.intellij.util.ui.UIUtil;
63 import com.intellij.util.ui.update.UiNotifyConnector;
64 import org.jdom.Element;
65 import org.jetbrains.annotations.NonNls;
66 import org.jetbrains.annotations.NotNull;
67 import org.jetbrains.annotations.Nullable;
68
69 import javax.swing.*;
70 import javax.swing.event.HyperlinkEvent;
71 import javax.swing.event.HyperlinkListener;
72 import java.awt.*;
73 import java.awt.event.InputEvent;
74 import java.awt.event.KeyEvent;
75 import java.beans.PropertyChangeEvent;
76 import java.beans.PropertyChangeListener;
77 import java.util.*;
78 import java.util.List;
79
80 /**
81  * @author Anton Katilin
82  * @author Vladimir Kondratyev
83  */
84 public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements ProjectComponent, JDOMExternalizable {
85   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.ToolWindowManagerImpl");
86
87   private final Project myProject;
88   private final WindowManagerEx myWindowManager;
89   private final EventDispatcher<ToolWindowManagerListener> myDispatcher = EventDispatcher.create(ToolWindowManagerListener.class);
90   private final DesktopLayout myLayout;
91   private final Map<String, InternalDecorator> myId2InternalDecorator;
92   private final Map<String, FloatingDecorator> myId2FloatingDecorator;
93   private final Map<String, StripeButton> myId2StripeButton;
94   private final Map<String, FocusWatcher> myId2FocusWatcher;
95   private final Set<String> myDumbAwareIds = Collections.synchronizedSet(ContainerUtil.<String>newTroveSet());
96
97   private final EditorComponentFocusWatcher myEditorComponentFocusWatcher;
98   private final MyToolWindowPropertyChangeListener myToolWindowPropertyChangeListener;
99   private final InternalDecoratorListener myInternalDecoratorListener;
100
101   private boolean myEditorWasActive;
102
103   private final ActiveStack myActiveStack;
104   private final SideStack mySideStack;
105
106   private ToolWindowsPane myToolWindowsPane;
107   private IdeFrameImpl myFrame;
108   private DesktopLayout myLayoutToRestoreLater = null;
109   @NonNls private static final String EDITOR_ELEMENT = "editor";
110   @NonNls private static final String ACTIVE_ATTR_VALUE = "active";
111   @NonNls private static final String FRAME_ELEMENT = "frame";
112   @NonNls private static final String X_ATTR = "x";
113   @NonNls private static final String Y_ATTR = "y";
114   @NonNls private static final String WIDTH_ATTR = "width";
115   @NonNls private static final String HEIGHT_ATTR = "height";
116   @NonNls private static final String EXTENDED_STATE_ATTR = "extended-state";
117   @NonNls private static final String LAYOUT_TO_RESTORE = "layout-to-restore";
118
119   private final FileEditorManager myFileEditorManager;
120   private final LafManager myLafManager;
121   private final Map<String, Balloon> myWindow2Balloon = new HashMap<String, Balloon>();
122   private Pair<String, Integer> myMaximizedToolwindowSize = null;
123
124   private KeyState myCurrentState = KeyState.waiting;
125   private final Alarm myWaiterForSecondPress = new Alarm();
126   private final Runnable mySecondPressRunnable = new Runnable() {
127     @Override
128     public void run() {
129       if (myCurrentState != KeyState.hold) {
130         resetHoldState();
131       }
132     }
133   };
134   private final PropertyChangeListener myFocusListener;
135
136   public boolean isToolWindowRegistered(String id) {
137     return myLayout.isToolWindowRegistered(id);
138   }
139
140   private enum KeyState {
141     waiting, pressed, released, hold
142   }
143
144   private final Alarm myUpdateHeadersAlarm = new Alarm();
145   private final Runnable myUpdateHeadersRunnable = new Runnable() {
146     @Override
147     public void run() {
148       updateToolWindowHeaders();
149     }
150   };
151
152   /**
153    * invoked by reflection
154    */
155   public ToolWindowManagerImpl(final Project project,
156                                final WindowManagerEx windowManagerEx,
157                                final FileEditorManager fem,
158                                final ActionManager actionManager,
159                                final LafManager lafManager) {
160     myProject = project;
161     myWindowManager = windowManagerEx;
162     myFileEditorManager = fem;
163     myLafManager = lafManager;
164
165     if (!project.isDefault()) {
166       actionManager.addAnActionListener(new AnActionListener() {
167         @Override
168         public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
169           if (myCurrentState != KeyState.hold) {
170             resetHoldState();
171           }
172         }
173
174         @Override
175         public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
176         }
177
178         @Override
179         public void beforeEditorTyping(char c, DataContext dataContext) {
180         }
181       }, project);
182     }
183
184     myLayout = new DesktopLayout();
185     myLayout.copyFrom(windowManagerEx.getLayout());
186
187     myId2InternalDecorator = new HashMap<String, InternalDecorator>();
188     myId2FloatingDecorator = new HashMap<String, FloatingDecorator>();
189     myId2StripeButton = new HashMap<String, StripeButton>();
190     myId2FocusWatcher = new HashMap<String, FocusWatcher>();
191
192     myEditorComponentFocusWatcher = new EditorComponentFocusWatcher();
193     myToolWindowPropertyChangeListener = new MyToolWindowPropertyChangeListener();
194     myInternalDecoratorListener = new MyInternalDecoratorListener();
195
196     myActiveStack = new ActiveStack();
197     mySideStack = new SideStack();
198
199     project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerListener() {
200       @Override
201       public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
202       }
203
204       @Override
205       public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
206         getFocusManagerImpl(myProject).doWhenFocusSettlesDown(new ExpirableRunnable.ForProject(myProject) {
207           @Override
208           public void run() {
209             if (!hasOpenEditorFiles()) {
210               focusToolWinowByDefault(null);
211             }
212           }
213         });
214       }
215
216       @Override
217       public void selectionChanged(@NotNull FileEditorManagerEvent event) {
218       }
219     });
220
221     myFocusListener = new PropertyChangeListener() {
222       @Override
223       public void propertyChange(PropertyChangeEvent evt) {
224         if ("focusOwner".equals(evt.getPropertyName())) {
225           myUpdateHeadersAlarm.cancelAllRequests();
226           myUpdateHeadersAlarm.addRequest(myUpdateHeadersRunnable, 50);
227         }
228       }
229     };
230     KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(myFocusListener);
231   }
232
233
234   private void updateToolWindowHeaders() {
235     getFocusManager().doWhenFocusSettlesDown(new ExpirableRunnable.ForProject(myProject) {
236       @Override
237       public void run() {
238         WindowInfoImpl[] infos = myLayout.getInfos();
239         for (WindowInfoImpl each : infos) {
240           if (each.isVisible()) {
241             ToolWindow tw = getToolWindow(each.getId());
242             if (tw instanceof ToolWindowImpl) {
243               InternalDecorator decorator = ((ToolWindowImpl)tw).getDecorator();
244               if (decorator != null) {
245                 decorator.repaint();
246               }
247             }
248           }
249         }
250       }
251     });
252   }
253
254   public boolean dispatchKeyEvent(KeyEvent e) {
255     if (e.getKeyCode() != KeyEvent.VK_CONTROL &&
256         e.getKeyCode() != KeyEvent.VK_ALT &&
257         e.getKeyCode() != KeyEvent.VK_SHIFT &&
258         e.getKeyCode() != KeyEvent.VK_META) {
259       if (e.getModifiers() == 0) {
260         resetHoldState();
261       }
262       return false;
263     }
264     if (e.getID() != KeyEvent.KEY_PRESSED && e.getID() != KeyEvent.KEY_RELEASED) return false;
265
266     Component parent = UIUtil.findUltimateParent(e.getComponent());
267     if (parent instanceof IdeFrame) {
268       if (((IdeFrame)parent).getProject() != myProject) {
269         resetHoldState();
270         return false;
271       }
272     }
273
274     Set<Integer> vks = getActivateToolWindowVKs();
275
276     if (vks.isEmpty()) {
277       resetHoldState();
278       return false;
279     }
280
281     if (vks.contains(e.getKeyCode())) {
282       boolean pressed = e.getID() == KeyEvent.KEY_PRESSED;
283       int modifiers = e.getModifiers();
284
285       int mouseMask = InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK | InputEvent.BUTTON3_DOWN_MASK;
286       if ((e.getModifiersEx() & mouseMask) == 0) {
287         if (SwitchManager.areAllModifiersPressed(modifiers, vks) || !pressed) {
288           processState(pressed);
289         }
290         else {
291           resetHoldState();
292         }
293       }
294     }
295
296
297     return false;
298   }
299
300   public static Set<Integer> getActivateToolWindowVKs() {
301     if (ApplicationManager.getApplication() == null) return new HashSet<Integer>();
302
303     Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
304     Shortcut[] baseShortcut = keymap.getShortcuts("ActivateProjectToolWindow");
305     int baseModifiers = 0;
306     for (Shortcut each : baseShortcut) {
307       if (each instanceof KeyboardShortcut) {
308         KeyStroke keyStroke = ((KeyboardShortcut)each).getFirstKeyStroke();
309         baseModifiers = keyStroke.getModifiers();
310         if (baseModifiers > 0) {
311           break;
312         }
313       }
314     }
315     return QuickAccessSettings.getModifiersVKs(baseModifiers);
316   }
317
318   private void resetHoldState() {
319     myCurrentState = KeyState.waiting;
320     processHoldState();
321   }
322
323   private void processState(boolean pressed) {
324     if (pressed) {
325       if (myCurrentState == KeyState.waiting) {
326         myCurrentState = KeyState.pressed;
327       }
328       else if (myCurrentState == KeyState.released) {
329         myCurrentState = KeyState.hold;
330         processHoldState();
331       }
332     }
333     else {
334       if (myCurrentState == KeyState.pressed) {
335         myCurrentState = KeyState.released;
336         restartWaitingForSecondPressAlarm();
337       }
338       else {
339         resetHoldState();
340       }
341     }
342   }
343
344   private void processHoldState() {
345     if (myToolWindowsPane != null) {
346       myToolWindowsPane.setStripesOverlayed(myCurrentState == KeyState.hold);
347     }
348   }
349
350   private void restartWaitingForSecondPressAlarm() {
351     myWaiterForSecondPress.cancelAllRequests();
352     myWaiterForSecondPress.addRequest(mySecondPressRunnable, Registry.intValue("actionSystem.keyGestureDblClickTime"));
353   }
354
355   private boolean hasOpenEditorFiles() {
356     return myFileEditorManager.getOpenFiles().length > 0;
357   }
358
359   private static IdeFocusManager getFocusManagerImpl(Project project) {
360     return IdeFocusManager.getInstance(project);
361   }
362
363   @NotNull
364   public Project getProject() {
365     return myProject;
366   }
367
368   @Override
369   public void initComponent() {
370   }
371
372   @Override
373   public void disposeComponent() {
374     for (String id : new ArrayList<String>(myId2StripeButton.keySet())) {
375       unregisterToolWindow(id);
376     }
377
378     assert myId2StripeButton.isEmpty();
379
380     KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener(myFocusListener);
381   }
382
383   @Override
384   public void projectOpened() {
385     final MyUIManagerPropertyChangeListener uiManagerPropertyListener = new MyUIManagerPropertyChangeListener();
386     final MyLafManagerListener lafManagerListener = new MyLafManagerListener();
387
388     UIManager.addPropertyChangeListener(uiManagerPropertyListener);
389     myLafManager.addLafManagerListener(lafManagerListener);
390
391     Disposer.register(myProject, new Disposable() {
392       @Override
393       public void dispose() {
394         UIManager.removePropertyChangeListener(uiManagerPropertyListener);
395         myLafManager.removeLafManagerListener(lafManagerListener);
396       }
397     });
398     myFrame = myWindowManager.allocateFrame(myProject);
399     LOG.assertTrue(myFrame != null);
400
401     final ArrayList<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
402
403     myToolWindowsPane = new ToolWindowsPane(myFrame, this);
404     Disposer.register(myProject, myToolWindowsPane);
405     ((IdeRootPane)myFrame.getRootPane()).setToolWindowsPane(myToolWindowsPane);
406     appendUpdateToolWindowsPaneCmd(commandsList);
407
408     myFrame.setTitle(FrameTitleBuilder.getInstance().getProjectTitle(myProject));
409     JComponent editorComponent = createEditorComponent(myProject);
410     myEditorComponentFocusWatcher.install(editorComponent);
411
412     appendSetEditorComponentCmd(editorComponent, commandsList);
413     if (myEditorWasActive && editorComponent instanceof EditorsSplitters) {
414       activateEditorComponentImpl(commandsList, true);
415     }
416     execute(commandsList);
417
418     final DumbService.DumbModeListener dumbModeListener = new DumbService.DumbModeListener() {
419       @Override
420       public void enteredDumbMode() {
421         disableStripeButtons();
422       }
423
424       @Override
425       public void exitDumbMode() {
426         for (final String id : getToolWindowIds()) {
427           getStripeButton(id).setEnabled(true);
428         }
429       }
430     };
431     myProject.getMessageBus().connect().subscribe(DumbService.DUMB_MODE, dumbModeListener);
432
433     StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() {
434       @Override
435       public void run() {
436         registerToolWindowsFromBeans();
437         if (DumbService.getInstance(myProject).isDumb()) {
438           disableStripeButtons();
439         }
440       }
441     });
442
443     IdeEventQueue.getInstance().addDispatcher(new IdeEventQueue.EventDispatcher() {
444       @Override
445       public boolean dispatch(AWTEvent e) {
446         if (e instanceof KeyEvent) {
447           dispatchKeyEvent((KeyEvent)e);
448         }
449         return false;
450       }
451     }, myProject);
452   }
453
454   private void disableStripeButtons() {
455     for (final String id : getToolWindowIds()) {
456       if (!myDumbAwareIds.contains(id)) {
457         if (isToolWindowVisible(id)) {
458           hideToolWindow(id, true);
459         }
460         StripeButton button = getStripeButton(id);
461         if (button != null) {
462           button.setEnabled(false);
463         }
464       }
465     }
466   }
467
468   private JComponent createEditorComponent(Project project) {
469     return FrameEditorComponentProvider.EP.getExtensions()[0].createEditorComponent(project);
470   }
471
472   private void registerToolWindowsFromBeans() {
473     ToolWindowEP[] beans = Extensions.getExtensions(ToolWindowEP.EP_NAME);
474     for (final ToolWindowEP bean : beans) {
475       final Condition<Project> condition = bean.getCondition();
476       if (condition == null || condition.value(myProject)) {
477         initToolWindow(bean);
478       }
479     }
480   }
481
482   @Override
483   public void initToolWindow(@NotNull ToolWindowEP bean) {
484     JLabel label = new JLabel("Initializing...", SwingConstants.CENTER);
485     label.setOpaque(true);
486     final Color treeBg = UIManager.getColor("Tree.background");
487     label.setBackground(ColorUtil.toAlpha(treeBg, 180));
488     final Color treeFg = UIUtil.getTreeForeground();
489     label.setForeground(ColorUtil.toAlpha(treeFg, 180));
490     ToolWindowAnchor toolWindowAnchor = ToolWindowAnchor.fromText(bean.anchor);
491     final ToolWindowFactory factory = bean.getToolWindowFactory();
492     final ToolWindowImpl toolWindow =
493       (ToolWindowImpl)registerToolWindow(bean.id, label, toolWindowAnchor, myProject, DumbService.isDumbAware(factory),
494                                          bean.canCloseContents);
495     toolWindow.setContentFactory(factory);
496     if (bean.icon != null && toolWindow.getIcon() == null) {
497       Icon icon = IconLoader.findIcon(bean.icon, factory.getClass());
498       if (icon == null) {
499         try {
500           icon = IconLoader.getIcon(bean.icon);
501         }
502         catch (Exception ignored) {
503         }
504       }
505       toolWindow.setIcon(icon);
506     }
507
508     WindowInfoImpl info = getInfo(bean.id);
509     if (!info.isSplit() && bean.secondary && !info.wasRead()) {
510       toolWindow.setSplitMode(bean.secondary, null);
511     }
512
513     final ActionCallback activation = toolWindow.setActivation(new ActionCallback());
514
515     final DumbAwareRunnable runnable = new DumbAwareRunnable() {
516       @Override
517       public void run() {
518         if (toolWindow.isDisposed()) return;
519
520         toolWindow.ensureContentInitialized();
521         activation.setDone();
522       }
523     };
524     if (ApplicationManager.getApplication().isUnitTestMode()) {
525       runnable.run();
526     }
527     else {
528       UiNotifyConnector.doWhenFirstShown(label, new Runnable() {
529         @Override
530         public void run() {
531           ApplicationManager.getApplication().invokeLater(runnable);
532         }
533       });
534     }
535   }
536
537   @Override
538   public void projectClosed() {
539     final ArrayList<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
540     final String[] ids = getToolWindowIds();
541
542     // Remove ToolWindowsPane
543     if (myFrame != null) {
544       ((IdeRootPane)myFrame.getRootPane()).setToolWindowsPane(null);
545       myWindowManager.releaseFrame(myFrame);
546     }
547     appendUpdateToolWindowsPaneCmd(commandsList);
548
549     // Hide all tool windows
550
551     for (final String id : ids) {
552       deactivateToolWindowImpl(id, true, commandsList);
553     }
554
555     // Remove editor component
556
557     final JComponent editorComponent = FileEditorManagerEx.getInstanceEx(myProject).getComponent();
558     myEditorComponentFocusWatcher.deinstall(editorComponent);
559     appendSetEditorComponentCmd(null, commandsList);
560     execute(commandsList);
561   }
562
563   @Override
564   public void addToolWindowManagerListener(@NotNull ToolWindowManagerListener l) {
565     myDispatcher.addListener(l);
566   }
567
568   @Override
569   public void addToolWindowManagerListener(@NotNull ToolWindowManagerListener l, @NotNull Disposable parentDisposable) {
570     myDispatcher.addListener(l, parentDisposable);
571   }
572
573   @Override
574   public void removeToolWindowManagerListener(@NotNull ToolWindowManagerListener l) {
575     myDispatcher.removeListener(l);
576   }
577
578   /**
579    * This is helper method. It delegated its functionality to the WindowManager.
580    * Before delegating it fires state changed.
581    */
582   public void execute(@NotNull List<FinalizableCommand> commandList) {
583     for (FinalizableCommand each : commandList) {
584       if (each.willChangeState()) {
585         fireStateChanged();
586         break;
587       }
588     }
589
590     for (FinalizableCommand each : commandList) {
591       each.beforeExecute(this);
592     }
593     myWindowManager.getCommandProcessor().execute(commandList, myProject.getDisposed());
594   }
595
596   @Override
597   public void activateEditorComponent() {
598     activateEditorComponent(true);
599   }
600
601   private void activateEditorComponent(final boolean forced) {
602     activateEditorComponent(forced, false); //TODO[kirillk]: runnable in activateEditorComponent(boolean, boolean) never runs
603   }
604
605   private void activateEditorComponent(final boolean forced, boolean now) {
606     if (LOG.isDebugEnabled()) {
607       LOG.debug("enter: activateEditorComponent()");
608     }
609     ApplicationManager.getApplication().assertIsDispatchThread();
610
611     final ExpirableRunnable runnable = new ExpirableRunnable.ForProject(myProject) {
612       @Override
613       public void run() {
614         final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
615         activateEditorComponentImpl(commandList, forced);
616         execute(commandList);
617       }
618     };
619     if (now) {
620       if (!runnable.isExpired()) {
621         runnable.run();
622       }
623     }
624     else {
625       final FocusRequestor requestor = getFocusManager().getFurtherRequestor();
626       getFocusManager().doWhenFocusSettlesDown(new ExpirableRunnable.ForProject(myProject) {
627         @Override
628         public void run() {
629           requestor.requestFocus(new FocusCommand() {
630             @NotNull
631             @Override
632             public ActionCallback run() {
633               runnable.run();
634               return new ActionCallback.Done();
635             }
636           }.setExpirable(runnable), forced);
637         }
638       });
639     }
640   }
641
642   private void activateEditorComponentImpl(List<FinalizableCommand> commandList, final boolean forced) {
643     final String active = getActiveToolWindowId();
644     // Now we have to request focus into most recent focused editor
645     appendRequestFocusInEditorComponentCmd(commandList, forced).doWhenDone(new Runnable() {
646       @Override
647       public void run() {
648         final ArrayList<FinalizableCommand> postExecute = new ArrayList<FinalizableCommand>();
649
650         if (LOG.isDebugEnabled()) {
651           LOG.debug("editor activated");
652         }
653         deactivateWindows(postExecute, null);
654         myActiveStack.clear();
655
656         execute(postExecute);
657       }
658     }).doWhenRejected(new Runnable() {
659       @Override
660       public void run() {
661         if (forced) {
662           getFocusManagerImpl(myProject).requestFocus(new FocusCommand() {
663             @NotNull
664             @Override
665             public ActionCallback run() {
666               final ArrayList<FinalizableCommand> cmds = new ArrayList<FinalizableCommand>();
667
668               final WindowInfoImpl toReactivate = getInfo(active);
669               final boolean reactivateLastActive = toReactivate != null && !isToHide(toReactivate);
670               deactivateWindows(cmds, reactivateLastActive ? active : null);
671               execute(cmds);
672
673               if (reactivateLastActive) {
674                 activateToolWindow(active, false, true);
675               }
676               else {
677                 if (active != null) {
678                   myActiveStack.remove(active, false);
679                 }
680
681                 if (!myActiveStack.isEmpty()) {
682                   activateToolWindow(myActiveStack.peek(), false, true);
683                 }
684               }
685               return new ActionCallback.Done();
686             }
687           }, false);
688         }
689       }
690     });
691   }
692
693   private void deactivateWindows(final ArrayList<FinalizableCommand> postExecute, @Nullable String idToIgnore) {
694     final WindowInfoImpl[] infos = myLayout.getInfos();
695     for (final WindowInfoImpl info : infos) {
696       final boolean shouldHide = isToHide(info);
697       if (idToIgnore != null && idToIgnore.equals(info.getId())) {
698         continue;
699       }
700       deactivateToolWindowImpl(info.getId(), shouldHide, postExecute);
701     }
702   }
703
704   private boolean isToHide(final WindowInfoImpl info) {
705     return (info.isAutoHide() || info.isSliding()) && !(info.isFloating() && hasModalChild(info));
706   }
707
708   /**
709    * Helper method. It makes window visible, activates it and request focus into the tool window.
710    * But it doesn't deactivate other tool windows. Use <code>prepareForActivation</code> method to
711    * deactivates other tool windows.
712    *
713    * @param dirtyMode if <code>true</code> then all UI operations are performed in "dirty" mode.
714    *                  It means that UI isn't validated and repainted just after each add/remove operation.
715    * @see ToolWindowManagerImpl#prepareForActivation
716    */
717   private void showAndActivate(final String id,
718                                final boolean dirtyMode,
719                                List<FinalizableCommand> commandsList,
720                                boolean autoFocusContents,
721                                boolean forcedFocusRequest) {
722     if (!getToolWindow(id).isAvailable()) {
723       return;
724     }
725     // show activated
726     final WindowInfoImpl info = getInfo(id);
727     boolean toApplyInfo = false;
728     if (!info.isActive()) {
729       info.setActive(true);
730       toApplyInfo = true;
731     }
732     showToolWindowImpl(id, dirtyMode, commandsList);
733
734     // activate
735     if (toApplyInfo) {
736       appendApplyWindowInfoCmd(info, commandsList);
737       myActiveStack.push(id);
738     }
739
740     if (autoFocusContents && ApplicationManager.getApplication().isActive()) {
741       appendRequestFocusInToolWindowCmd(id, commandsList, forcedFocusRequest);
742     }
743   }
744
745   void activateToolWindow(final String id, boolean forced, boolean autoFocusContents) {
746     if (LOG.isDebugEnabled()) {
747       LOG.debug("enter: activateToolWindow(" + id + ")");
748     }
749     ApplicationManager.getApplication().assertIsDispatchThread();
750     checkId(id);
751     if (DumbService.getInstance(myProject).isDumb() && !myDumbAwareIds.contains(id)) {
752       return;
753     }
754
755     final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
756     activateToolWindowImpl(id, commandList, forced, autoFocusContents);
757     execute(commandList);
758   }
759
760   private void activateToolWindowImpl(final String id,
761                                       List<FinalizableCommand> commandList,
762                                       boolean forced,
763                                       boolean autoFocusContents) {
764     if (!FocusManagerImpl.getInstance().isUnforcedRequestAllowed() && !forced) return;
765
766     if (LOG.isDebugEnabled()) {
767       LOG.debug("enter: activateToolWindowImpl(" + id + ")");
768     }
769     if (!getToolWindow(id).isAvailable()) {
770       // Tool window can be "logically" active but not focused. For example,
771       // when the user switched to another application. So we just need to bring
772       // tool window's window to front.
773       final InternalDecorator decorator = getInternalDecorator(id);
774       if (!decorator.hasFocus() && autoFocusContents) {
775         appendRequestFocusInToolWindowCmd(id, commandList, forced);
776       }
777       return;
778     }
779     prepareForActivation(id, commandList);
780     showAndActivate(id, false, commandList, autoFocusContents, forced);
781   }
782
783   /**
784    * Checkes whether the specified <code>id</code> defines installed tool
785    * window. If it's not then throws <code>IllegalStateException</code>.
786    *
787    * @throws IllegalStateException if tool window isn't installed.
788    */
789   private void checkId(final String id) {
790     if (!myLayout.isToolWindowRegistered(id)) {
791       throw new IllegalStateException("window with id=\"" + id + "\" isn't registered");
792     }
793   }
794
795   /**
796    * Helper method. It deactivates (and hides) window with specified <code>id</code>.
797    *
798    * @param id         <code>id</code> of the tool window to be deactivated.
799    * @param shouldHide if <code>true</code> then also hides specified tool window.
800    */
801   private void deactivateToolWindowImpl(final String id, final boolean shouldHide, final List<FinalizableCommand> commandsList) {
802     if (LOG.isDebugEnabled()) {
803       LOG.debug("enter: deactivateToolWindowImpl(" + id + "," + shouldHide + ")");
804     }
805     final WindowInfoImpl info = getInfo(id);
806     if (shouldHide && info.isVisible()) {
807       info.setVisible(false);
808       if (info.isFloating()) {
809         appendRemoveFloatingDecoratorCmd(info, commandsList);
810       }
811       else { // docked and sliding windows
812         appendRemoveDecoratorCmd(id, false, commandsList);
813       }
814     }
815     info.setActive(false);
816     appendApplyWindowInfoCmd(info, commandsList);
817   }
818
819   @NotNull
820   @Override
821   public String[] getToolWindowIds() {
822     final WindowInfoImpl[] infos = myLayout.getInfos();
823     final String[] ids = ArrayUtil.newStringArray(infos.length);
824     for (int i = 0; i < infos.length; i++) {
825       ids[i] = infos[i].getId();
826     }
827     return ids;
828   }
829
830   @Override
831   public String getActiveToolWindowId() {
832     ApplicationManager.getApplication().assertIsDispatchThread();
833     return myLayout.getActiveId();
834   }
835
836   @Override
837   public String getLastActiveToolWindowId() {
838     return getLastActiveToolWindowId(null);
839   }
840
841   @Override
842   @Nullable
843   public String getLastActiveToolWindowId(@Nullable Condition<JComponent> condition) {
844     ApplicationManager.getApplication().assertIsDispatchThread();
845     String lastActiveToolWindowId = null;
846     for (int i = 0; i < myActiveStack.getPersistentSize(); i++) {
847       final String id = myActiveStack.peekPersistent(i);
848       final ToolWindow toolWindow = getToolWindow(id);
849       LOG.assertTrue(toolWindow != null);
850       if (toolWindow.isAvailable()) {
851         if (condition == null || condition.value(toolWindow.getComponent())) {
852           lastActiveToolWindowId = id;
853           break;
854         }
855       }
856     }
857     return lastActiveToolWindowId;
858   }
859
860   /**
861    * @return floating decorator for the tool window with specified <code>ID</code>.
862    */
863   private FloatingDecorator getFloatingDecorator(final String id) {
864     return myId2FloatingDecorator.get(id);
865   }
866
867   /**
868    * @return internal decorator for the tool window with specified <code>ID</code>.
869    */
870   private InternalDecorator getInternalDecorator(final String id) {
871     return myId2InternalDecorator.get(id);
872   }
873
874   /**
875    * @return tool button for the window with specified <code>ID</code>.
876    */
877   private StripeButton getStripeButton(final String id) {
878     return myId2StripeButton.get(id);
879   }
880
881   /**
882    * @return info for the tool window with specified <code>ID</code>.
883    */
884   private WindowInfoImpl getInfo(final String id) {
885     return myLayout.getInfo(id, true);
886   }
887
888   @Override
889   public List<String> getIdsOn(@NotNull final ToolWindowAnchor anchor) {
890     return myLayout.getVisibleIdsOn(anchor, this);
891   }
892
893   @Override
894   public ToolWindow getToolWindow(final String id) {
895     if (!myLayout.isToolWindowRegistered(id)) {
896       return null;
897     }
898     InternalDecorator decorator = getInternalDecorator(id);
899     return decorator != null ? decorator.getToolWindow() : null;
900   }
901
902   void showToolWindow(final String id) {
903     if (LOG.isDebugEnabled()) {
904       LOG.debug("enter: showToolWindow(" + id + ")");
905     }
906     ApplicationManager.getApplication().assertIsDispatchThread();
907     final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
908     showToolWindowImpl(id, false, commandList);
909     execute(commandList);
910   }
911
912   @Override
913   public void hideToolWindow(@NotNull final String id, final boolean hideSide) {
914     hideToolWindow(id, hideSide, true);
915   }
916
917   public void hideToolWindow(final String id, final boolean hideSide, final boolean moveFocus) {
918     ApplicationManager.getApplication().assertIsDispatchThread();
919     checkId(id);
920     final WindowInfoImpl info = getInfo(id);
921     if (!info.isVisible()) return;
922     final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
923     final boolean wasActive = info.isActive();
924
925     // hide and deactivate
926
927     deactivateToolWindowImpl(id, true, commandList);
928
929     if (hideSide && !info.isFloating()) {
930       final List<String> ids = myLayout.getVisibleIdsOn(info.getAnchor(), this);
931       for (String each : ids) {
932         myActiveStack.remove(each, true);
933       }
934
935
936       while (!mySideStack.isEmpty(info.getAnchor())) {
937         mySideStack.pop(info.getAnchor());
938       }
939
940       final String[] all = getToolWindowIds();
941       for (String eachId : all) {
942         final WindowInfoImpl eachInfo = getInfo(eachId);
943         if (eachInfo.isVisible() && eachInfo.getAnchor() == info.getAnchor()) {
944           deactivateToolWindowImpl(eachId, true, commandList);
945         }
946       }
947
948       activateEditorComponentImpl(commandList, true);
949     }
950     else if (isStackEnabled()) {
951
952       // first of all we have to find tool window that was located at the same side and
953       // was hidden.
954
955       WindowInfoImpl info2 = null;
956       while (!mySideStack.isEmpty(info.getAnchor())) {
957         final WindowInfoImpl storedInfo = mySideStack.pop(info.getAnchor());
958         if (storedInfo.isSplit() != info.isSplit()) {
959           continue;
960         }
961
962         final WindowInfoImpl currentInfo = getInfo(storedInfo.getId());
963         LOG.assertTrue(currentInfo != null);
964         // SideStack contains copies of real WindowInfos. It means that
965         // these stored infos can be invalid. The following loop removes invalid WindowInfos.
966         if (storedInfo.getAnchor() == currentInfo.getAnchor() &&
967             storedInfo.getType() == currentInfo.getType() &&
968             storedInfo.isAutoHide() == currentInfo.isAutoHide()) {
969           info2 = storedInfo;
970           break;
971         }
972       }
973       if (info2 != null) {
974         showToolWindowImpl(info2.getId(), false, commandList);
975       }
976
977       // If we hide currently active tool window then we should activate the previous
978       // one which is located in the tool window stack.
979       // Activate another tool window if no active tool window exists and
980       // window stack is enabled.
981
982       myActiveStack.remove(id, false); // hidden window should be at the top of stack
983
984       if (wasActive && moveFocus) {
985         if (myActiveStack.isEmpty()) {
986           if (hasOpenEditorFiles()) {
987             activateEditorComponentImpl(commandList, false);
988           }
989           else {
990             focusToolWinowByDefault(id);
991           }
992         }
993         else {
994           final String toBeActivatedId = myActiveStack.pop();
995           if (toBeActivatedId != null && (getInfo(toBeActivatedId).isVisible() || isStackEnabled())) {
996             activateToolWindowImpl(toBeActivatedId, commandList, false, true);
997           }
998           else {
999             focusToolWinowByDefault(id);
1000           }
1001         }
1002       }
1003     }
1004     //todo[kb] it's just a temporary solution due a number of focus issues in JDK 7
1005     if (SystemInfo.isJavaVersionAtLeast("1.7")) {
1006       if (hasOpenEditorFiles()) {
1007         activateEditorComponentImpl(commandList, false);
1008       }
1009       else {
1010         focusToolWinowByDefault(id);
1011       }
1012     }
1013
1014     execute(commandList);
1015   }
1016
1017   private static boolean isStackEnabled() {
1018     return Registry.is("ide.enable.toolwindow.stack");
1019   }
1020
1021   /**
1022    * @param dirtyMode if <code>true</code> then all UI operations are performed in dirty mode.
1023    */
1024   private void showToolWindowImpl(final String id, final boolean dirtyMode, final List<FinalizableCommand> commandsList) {
1025     final WindowInfoImpl toBeShownInfo = getInfo(id);
1026     if (toBeShownInfo.isVisible() || !getToolWindow(id).isAvailable()) {
1027       return;
1028     }
1029
1030     if (DumbService.getInstance(myProject).isDumb() && !myDumbAwareIds.contains(id)) {
1031       return;
1032     }
1033
1034     toBeShownInfo.setVisible(true);
1035     final InternalDecorator decorator = getInternalDecorator(id);
1036
1037     if (toBeShownInfo.isFloating()) {
1038       commandsList.add(new AddFloatingDecoratorCmd(decorator, toBeShownInfo));
1039     }
1040     else { // docked and sliding windows
1041
1042       // If there is tool window on the same side then we have to hide it, i.e.
1043       // clear place for tool window to be shown.
1044       //
1045       // We store WindowInfo of hidden tool window in the SideStack (if the tool window
1046       // is docked and not auto-hide one). Therefore it's possible to restore the
1047       // hidden tool window when showing tool window will be closed.
1048
1049       final WindowInfoImpl[] infos = myLayout.getInfos();
1050       for (final WindowInfoImpl info : infos) {
1051         if (id.equals(info.getId())) {
1052           continue;
1053         }
1054         if (info.isVisible() &&
1055             info.getType() == toBeShownInfo.getType() &&
1056             info.getAnchor() == toBeShownInfo.getAnchor() &&
1057             info.isSplit() == toBeShownInfo.isSplit()) {
1058           // hide and deactivate tool window
1059           info.setVisible(false);
1060           appendRemoveDecoratorCmd(info.getId(), false, commandsList);
1061           if (info.isActive()) {
1062             info.setActive(false);
1063           }
1064           appendApplyWindowInfoCmd(info, commandsList);
1065           // store WindowInfo into the SideStack
1066           if (info.isDocked() && !info.isAutoHide()) {
1067             mySideStack.push(info);
1068           }
1069         }
1070       }
1071       appendAddDecoratorCmd(decorator, toBeShownInfo, dirtyMode, commandsList);
1072
1073       // Remove tool window from the SideStack.
1074
1075       mySideStack.remove(id);
1076     }
1077
1078     appendApplyWindowInfoCmd(toBeShownInfo, commandsList);
1079   }
1080
1081   @NotNull
1082   @Override
1083   public ToolWindow registerToolWindow(@NotNull final String id,
1084                                        @NotNull final JComponent component,
1085                                        @NotNull final ToolWindowAnchor anchor) {
1086     return registerToolWindow(id, component, anchor, false);
1087   }
1088
1089   @NotNull
1090   @Override
1091   public ToolWindow registerToolWindow(@NotNull final String id,
1092                                        @NotNull JComponent component,
1093                                        @NotNull ToolWindowAnchor anchor,
1094                                        @NotNull Disposable parentDisposable) {
1095     return registerToolWindow(id, component, anchor, parentDisposable, false, false);
1096   }
1097
1098   @NotNull
1099   @Override
1100   public ToolWindow registerToolWindow(@NotNull String id,
1101                                        @NotNull JComponent component,
1102                                        @NotNull ToolWindowAnchor anchor,
1103                                        @NotNull Disposable parentDisposable,
1104                                        boolean canWorkInDumbMode) {
1105     return registerToolWindow(id, component, anchor, parentDisposable, canWorkInDumbMode, false);
1106   }
1107
1108   @NotNull
1109   @Override
1110   public ToolWindow registerToolWindow(@NotNull final String id,
1111                                        @NotNull JComponent component,
1112                                        @NotNull ToolWindowAnchor anchor,
1113                                        @NotNull Disposable parentDisposable,
1114                                        boolean canWorkInDumbMode, boolean canCloseContents) {
1115     return registerDisposable(id, parentDisposable, registerToolWindow(id, component, anchor, false, canCloseContents, canWorkInDumbMode));
1116   }
1117
1118   @NotNull
1119   private ToolWindow registerToolWindow(@NotNull final String id,
1120                                         @NotNull final JComponent component,
1121                                         @NotNull final ToolWindowAnchor anchor,
1122                                         boolean canWorkInDumbMode) {
1123     return registerToolWindow(id, component, anchor, false, false, canWorkInDumbMode);
1124   }
1125
1126   @NotNull
1127   @Override
1128   public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor) {
1129     return registerToolWindow(id, null, anchor, false, canCloseContent, false);
1130   }
1131
1132   @NotNull
1133   @Override
1134   public ToolWindow registerToolWindow(@NotNull final String id,
1135                                        final boolean canCloseContent,
1136                                        @NotNull final ToolWindowAnchor anchor,
1137                                        final boolean secondary) {
1138     return registerToolWindow(id, null, anchor, secondary, canCloseContent, false);
1139   }
1140
1141
1142   @NotNull
1143   @Override
1144   public ToolWindow registerToolWindow(@NotNull final String id,
1145                                        final boolean canCloseContent,
1146                                        @NotNull final ToolWindowAnchor anchor,
1147                                        @NotNull final Disposable parentDisposable,
1148                                        final boolean canWorkInDumbMode) {
1149     ToolWindow window = registerToolWindow(id, null, anchor, false, canCloseContent, canWorkInDumbMode);
1150     return registerDisposable(id, parentDisposable, window);
1151   }
1152
1153   @NotNull
1154   private ToolWindow registerToolWindow(@NotNull final String id,
1155                                         @Nullable final JComponent component,
1156                                         @NotNull final ToolWindowAnchor anchor,
1157                                         boolean sideTool,
1158                                         boolean canCloseContent,
1159                                         final boolean canWorkInDumbMode) {
1160     if (LOG.isDebugEnabled()) {
1161       LOG.debug("enter: installToolWindow(" + id + "," + component + "," + anchor + "\")");
1162     }
1163     ApplicationManager.getApplication().assertIsDispatchThread();
1164     if (myLayout.isToolWindowRegistered(id)) {
1165       throw new IllegalArgumentException("window with id=\"" + id + "\" is already registered");
1166     }
1167
1168     final WindowInfoImpl info = myLayout.register(id, anchor, sideTool);
1169     final boolean wasActive = info.isActive();
1170     final boolean wasVisible = info.isVisible();
1171     info.setActive(false);
1172     info.setVisible(false);
1173
1174     // Create decorator
1175
1176     ToolWindowImpl toolWindow = new ToolWindowImpl(this, id, canCloseContent, component);
1177     InternalDecorator decorator = new InternalDecorator(myProject, info.copy(), toolWindow);
1178     ActivateToolWindowAction.ensureToolWindowActionRegistered(toolWindow);
1179     myId2InternalDecorator.put(id, decorator);
1180     decorator.addInternalDecoratorListener(myInternalDecoratorListener);
1181     toolWindow.addPropertyChangeListener(myToolWindowPropertyChangeListener);
1182     myId2FocusWatcher.put(id, new ToolWindowFocusWatcher(toolWindow));
1183
1184     // Create and show tool button
1185
1186     final StripeButton button = new StripeButton(decorator, myToolWindowsPane);
1187     myId2StripeButton.put(id, button);
1188     List<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
1189     appendAddButtonCmd(button, info, commandsList);
1190
1191     if (canWorkInDumbMode) {
1192       myDumbAwareIds.add(id);
1193     }
1194     else if (DumbService.getInstance(getProject()).isDumb()) {
1195       button.setEnabled(false);
1196     }
1197
1198     // If preloaded info is visible or active then we have to show/activate the installed
1199     // tool window. This step has sense only for windows which are not in the autohide
1200     // mode. But if tool window was active but its mode doen't allow to activate it again
1201     // (for example, tool window is in autohide mode) then we just activate editor component.
1202
1203     if (!info.isAutoHide() && (info.isDocked() || info.isFloating())) {
1204       if (wasActive) {
1205         activateToolWindowImpl(info.getId(), commandsList, true, true);
1206       }
1207       else if (wasVisible) {
1208         showToolWindowImpl(info.getId(), false, commandsList);
1209       }
1210     }
1211     else if (wasActive) { // tool window was active but it cannot be activate again
1212       activateEditorComponentImpl(commandsList, true);
1213     }
1214
1215     execute(commandsList);
1216     fireToolWindowRegistered(id);
1217     return toolWindow;
1218   }
1219
1220   @NotNull
1221   private ToolWindow registerDisposable(@NotNull final String id, @NotNull final Disposable parentDisposable, @NotNull ToolWindow window) {
1222     Disposer.register(parentDisposable, new Disposable() {
1223       @Override
1224       public void dispose() {
1225         unregisterToolWindow(id);
1226       }
1227     });
1228     return window;
1229   }
1230
1231   @Override
1232   public void unregisterToolWindow(@NotNull final String id) {
1233     if (LOG.isDebugEnabled()) {
1234       LOG.debug("enter: unregisterToolWindow(" + id + ")");
1235     }
1236     ApplicationManager.getApplication().assertIsDispatchThread();
1237     if (!myLayout.isToolWindowRegistered(id)) {
1238       return;
1239     }
1240
1241     final WindowInfoImpl info = getInfo(id);
1242     final ToolWindowEx toolWindow = (ToolWindowEx)getToolWindow(id);
1243     // Save recent appearance of tool window
1244     myLayout.unregister(id);
1245     // Remove decorator and tool button from the screen
1246     final ArrayList<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
1247     if (info.isVisible()) {
1248       info.setVisible(false);
1249       if (info.isFloating()) {
1250         appendRemoveFloatingDecoratorCmd(info, commandsList);
1251       }
1252       else { // floating and sliding windows
1253         appendRemoveDecoratorCmd(id, false, commandsList);
1254       }
1255     }
1256     appendRemoveButtonCmd(id, commandsList);
1257     appendApplyWindowInfoCmd(info, commandsList);
1258     execute(commandsList);
1259     // Remove all references on tool window and save its last properties
1260     toolWindow.removePropertyChangeListener(myToolWindowPropertyChangeListener);
1261     myActiveStack.remove(id, true);
1262     mySideStack.remove(id);
1263     // Destroy stripe button
1264     final StripeButton button = getStripeButton(id);
1265     button.dispose();
1266     myId2StripeButton.remove(id);
1267     //
1268     ToolWindowFocusWatcher watcher = (ToolWindowFocusWatcher)myId2FocusWatcher.remove(id);
1269     watcher.deinstall();
1270
1271     // Destroy decorator
1272     final InternalDecorator decorator = getInternalDecorator(id);
1273     decorator.dispose();
1274     decorator.removeInternalDecoratorListener(myInternalDecoratorListener);
1275     myId2InternalDecorator.remove(id);
1276   }
1277
1278   @Override
1279   public DesktopLayout getLayout() {
1280     ApplicationManager.getApplication().assertIsDispatchThread();
1281     return myLayout;
1282   }
1283
1284   @Override
1285   public void setLayoutToRestoreLater(DesktopLayout layout) {
1286     myLayoutToRestoreLater = layout;
1287   }
1288
1289   @Override
1290   public DesktopLayout getLayoutToRestoreLater() {
1291     return myLayoutToRestoreLater;
1292   }
1293
1294   @Override
1295   public void setLayout(@NotNull final DesktopLayout layout) {
1296     ApplicationManager.getApplication().assertIsDispatchThread();
1297     final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1298     // hide tool window that are invisible in new layout
1299     final WindowInfoImpl[] currentInfos = myLayout.getInfos();
1300     for (final WindowInfoImpl currentInfo : currentInfos) {
1301       final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1302       if (info == null) {
1303         continue;
1304       }
1305       if (currentInfo.isVisible() && !info.isVisible()) {
1306         deactivateToolWindowImpl(currentInfo.getId(), true, commandList);
1307       }
1308     }
1309     // change anchor of tool windows
1310     for (final WindowInfoImpl currentInfo : currentInfos) {
1311       final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1312       if (info == null) {
1313         continue;
1314       }
1315       if (currentInfo.getAnchor() != info.getAnchor() || currentInfo.getOrder() != info.getOrder()) {
1316         setToolWindowAnchorImpl(currentInfo.getId(), info.getAnchor(), info.getOrder(), commandList);
1317       }
1318     }
1319     // change types of tool windows
1320     for (final WindowInfoImpl currentInfo : currentInfos) {
1321       final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1322       if (info == null) {
1323         continue;
1324       }
1325       if (currentInfo.getType() != info.getType()) {
1326         setToolWindowTypeImpl(currentInfo.getId(), info.getType(), commandList);
1327       }
1328     }
1329     // change auto-hide state
1330     for (final WindowInfoImpl currentInfo : currentInfos) {
1331       final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1332       if (info == null) {
1333         continue;
1334       }
1335       if (currentInfo.isAutoHide() != info.isAutoHide()) {
1336         setToolWindowAutoHideImpl(currentInfo.getId(), info.isAutoHide(), commandList);
1337       }
1338     }
1339     // restore visibility
1340     for (final WindowInfoImpl currentInfo : currentInfos) {
1341       final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1342       if (info == null) {
1343         continue;
1344       }
1345       if (info.isVisible()) {
1346         showToolWindowImpl(currentInfo.getId(), false, commandList);
1347       }
1348     }
1349     // if there is no any active tool window and editor is also inactive
1350     // then activate editor
1351     if (!myEditorWasActive && getActiveToolWindowId() == null) {
1352       activateEditorComponentImpl(commandList, true);
1353     }
1354     execute(commandList);
1355   }
1356
1357   @Override
1358   public void invokeLater(@NotNull final Runnable runnable) {
1359     List<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1360     commandList.add(new InvokeLaterCmd(runnable, myWindowManager.getCommandProcessor()));
1361     execute(commandList);
1362   }
1363
1364   @NotNull
1365   @Override
1366   public IdeFocusManager getFocusManager() {
1367     return IdeFocusManager.getInstance(myProject);
1368   }
1369
1370   @Override
1371   public boolean canShowNotification(@NotNull final String toolWindowId) {
1372     if (!Arrays.asList(getToolWindowIds()).contains(toolWindowId)) {
1373       return false;
1374     }
1375     final Stripe stripe = myToolWindowsPane.getStripeFor(toolWindowId);
1376     return stripe != null && stripe.getButtonFor(toolWindowId) != null;
1377   }
1378
1379   @Override
1380   public void notifyByBalloon(@NotNull final String toolWindowId, @NotNull final MessageType type, @NotNull final String htmlBody) {
1381     notifyByBalloon(toolWindowId, type, htmlBody, null, null);
1382   }
1383
1384   @Override
1385   public void notifyByBalloon(@NotNull final String toolWindowId,
1386                               @NotNull final MessageType type,
1387                               @NotNull final String text,
1388                               @Nullable final Icon icon,
1389                               @Nullable final HyperlinkListener listener) {
1390     checkId(toolWindowId);
1391
1392
1393     Balloon existing = myWindow2Balloon.get(toolWindowId);
1394     if (existing != null) {
1395       existing.hide();
1396     }
1397
1398     final Stripe stripe = myToolWindowsPane.getStripeFor(toolWindowId);
1399     if (stripe == null) {
1400       return;
1401     }
1402     final ToolWindowImpl window = getInternalDecorator(toolWindowId).getToolWindow();
1403     if (!window.isAvailable()) {
1404       window.setPlaceholderMode(true);
1405       stripe.updatePresentation();
1406       stripe.revalidate();
1407       stripe.repaint();
1408     }
1409
1410     final ToolWindowAnchor anchor = getInfo(toolWindowId).getAnchor();
1411     final Ref<Balloon.Position> position = Ref.create(Balloon.Position.below);
1412     if (ToolWindowAnchor.TOP == anchor) {
1413       position.set(Balloon.Position.below);
1414     }
1415     else if (ToolWindowAnchor.BOTTOM == anchor) {
1416       position.set(Balloon.Position.above);
1417     }
1418     else if (ToolWindowAnchor.LEFT == anchor) {
1419       position.set(Balloon.Position.atRight);
1420     }
1421     else if (ToolWindowAnchor.RIGHT == anchor) {
1422       position.set(Balloon.Position.atLeft);
1423     }
1424
1425     final BalloonHyperlinkListener listenerWrapper = new BalloonHyperlinkListener(listener);
1426     final Balloon balloon =
1427       JBPopupFactory.getInstance()
1428         .createHtmlTextBalloonBuilder(text.replace("\n", "<br>"), icon, type.getPopupBackground(), listenerWrapper)
1429         .setHideOnClickOutside(false).setHideOnFrameResize(false).createBalloon();
1430     FrameStateManager.getInstance().getApplicationActive().doWhenDone(new Runnable() {
1431       @Override
1432       public void run() {
1433         final Alarm alarm = new Alarm();
1434         alarm.addRequest(new Runnable() {
1435           @Override
1436           public void run() {
1437             ((BalloonImpl)balloon).setHideOnClickOutside(true);
1438             Disposer.dispose(alarm);
1439           }
1440         }, 100);
1441       }
1442     });
1443     listenerWrapper.myBalloon = balloon;
1444     myWindow2Balloon.put(toolWindowId, balloon);
1445     Disposer.register(balloon, new Disposable() {
1446       @Override
1447       public void dispose() {
1448         window.setPlaceholderMode(false);
1449         stripe.updatePresentation();
1450         stripe.revalidate();
1451         stripe.repaint();
1452         myWindow2Balloon.remove(toolWindowId);
1453       }
1454     });
1455     Disposer.register(getProject(), balloon);
1456
1457     execute(new ArrayList<FinalizableCommand>(Arrays.<FinalizableCommand>asList(new FinalizableCommand(null) {
1458       @Override
1459       public void run() {
1460         final StripeButton button = stripe.getButtonFor(toolWindowId);
1461         LOG.assertTrue(button != null, "Button was not found, popup won't be shown. Toolwindow id: " +
1462                                        toolWindowId +
1463                                        ", message: " +
1464                                        text +
1465                                        ", message type: " +
1466                                        type);
1467         if (button == null) return;
1468
1469         final Runnable show = new Runnable() {
1470           @Override
1471           public void run() {
1472             if (button.isShowing()) {
1473               PositionTracker<Balloon> tracker = new PositionTracker<Balloon>(button) {
1474                 @Override
1475                 @Nullable
1476                 public RelativePoint recalculateLocation(Balloon object) {
1477                   Stripe twStripe = myToolWindowsPane.getStripeFor(toolWindowId);
1478                   StripeButton twButton = twStripe != null ? twStripe.getButtonFor(toolWindowId) : null;
1479
1480                   if (twButton == null) return null;
1481
1482                   if (getToolWindow(toolWindowId).getAnchor() != anchor) {
1483                     object.hide();
1484                     return null;
1485                   }
1486
1487                   final Point point = new Point(twButton.getBounds().width / 2, twButton.getHeight() / 2 - 2);
1488                   return new RelativePoint(twButton, point);
1489                 }
1490               };
1491               if (!balloon.isDisposed()) {
1492                 balloon.show(tracker, position.get());
1493               }
1494             }
1495             else {
1496               final Rectangle bounds = myToolWindowsPane.getBounds();
1497               final Point target = UIUtil.getCenterPoint(bounds, new Dimension(1, 1));
1498               if (ToolWindowAnchor.TOP == anchor) {
1499                 target.y = 0;
1500               }
1501               else if (ToolWindowAnchor.BOTTOM == anchor) {
1502                 target.y = bounds.height - 3;
1503               }
1504               else if (ToolWindowAnchor.LEFT == anchor) {
1505                 target.x = 0;
1506               }
1507               else if (ToolWindowAnchor.RIGHT == anchor) {
1508                 target.x = bounds.width;
1509               }
1510               if (!balloon.isDisposed()) {
1511                 balloon.show(new RelativePoint(myToolWindowsPane, target), position.get());
1512               }
1513             }
1514           }
1515         };
1516
1517         if (!button.isValid()) {
1518           SwingUtilities.invokeLater(new Runnable() {
1519             @Override
1520             public void run() {
1521               show.run();
1522             }
1523           });
1524         }
1525         else {
1526           show.run();
1527         }
1528       }
1529     })));
1530   }
1531
1532   @Override
1533   public Balloon getToolWindowBalloon(String id) {
1534     return myWindow2Balloon.get(id);
1535   }
1536
1537   @Override
1538   public boolean isEditorComponentActive() {
1539     ApplicationManager.getApplication().assertIsDispatchThread();
1540
1541     Component owner = getFocusManager().getFocusOwner();
1542     EditorsSplitters splitters = UIUtil.getParentOfType(EditorsSplitters.class, owner);
1543     return splitters != null;
1544   }
1545
1546   ToolWindowAnchor getToolWindowAnchor(final String id) {
1547     checkId(id);
1548     return getInfo(id).getAnchor();
1549   }
1550
1551   void setToolWindowAnchor(final String id, final ToolWindowAnchor anchor) {
1552     ApplicationManager.getApplication().assertIsDispatchThread();
1553     setToolWindowAnchor(id, anchor, -1);
1554   }
1555
1556   void setToolWindowAnchor(final String id, final ToolWindowAnchor anchor, final int order) {
1557     ApplicationManager.getApplication().assertIsDispatchThread();
1558     final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1559     setToolWindowAnchorImpl(id, anchor, order, commandList);
1560     execute(commandList);
1561   }
1562
1563   private void setToolWindowAnchorImpl(final String id,
1564                                        final ToolWindowAnchor anchor,
1565                                        final int order,
1566                                        final ArrayList<FinalizableCommand> commandsList) {
1567     checkId(id);
1568     final WindowInfoImpl info = getInfo(id);
1569     if (anchor == info.getAnchor() && order == info.getOrder()) {
1570       return;
1571     }
1572     // if tool window isn't visible or only order number is changed then just remove/add stripe button
1573     if (!info.isVisible() || anchor == info.getAnchor() || info.isFloating()) {
1574       appendRemoveButtonCmd(id, commandsList);
1575       myLayout.setAnchor(id, anchor, order);
1576       // update infos for all window. Actually we have to update only infos affected by
1577       // setAnchor method
1578       final WindowInfoImpl[] infos = myLayout.getInfos();
1579       for (WindowInfoImpl info1 : infos) {
1580         appendApplyWindowInfoCmd(info1, commandsList);
1581       }
1582       appendAddButtonCmd(getStripeButton(id), info, commandsList);
1583     }
1584     else { // for docked and sliding windows we have to move buttons and window's decorators
1585       info.setVisible(false);
1586       appendRemoveDecoratorCmd(id, false, commandsList);
1587       appendRemoveButtonCmd(id, commandsList);
1588       myLayout.setAnchor(id, anchor, order);
1589       // update infos for all window. Actually we have to update only infos affected by
1590       // setAnchor method
1591       final WindowInfoImpl[] infos = myLayout.getInfos();
1592       for (WindowInfoImpl info1 : infos) {
1593         appendApplyWindowInfoCmd(info1, commandsList);
1594       }
1595       appendAddButtonCmd(getStripeButton(id), info, commandsList);
1596       showToolWindowImpl(id, false, commandsList);
1597       if (info.isActive()) {
1598         appendRequestFocusInToolWindowCmd(id, commandsList, true);
1599       }
1600     }
1601   }
1602
1603   boolean isSplitMode(String id) {
1604     ApplicationManager.getApplication().assertIsDispatchThread();
1605     checkId(id);
1606     return getInfo(id).isSplit();
1607   }
1608
1609   ToolWindowContentUiType getContentUiType(String id) {
1610     ApplicationManager.getApplication().assertIsDispatchThread();
1611     checkId(id);
1612     return getInfo(id).getContentUiType();
1613   }
1614
1615   void setSideTool(String id, boolean isSide) {
1616     final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1617     setSplitModeImpl(id, isSide, commandList);
1618     execute(commandList);
1619   }
1620
1621   public void setContentUiType(String id, ToolWindowContentUiType type) {
1622     final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1623     checkId(id);
1624     WindowInfoImpl info = getInfo(id);
1625     info.setContentUiType(type);
1626     appendApplyWindowInfoCmd(info, commandList);
1627     execute(commandList);
1628   }
1629
1630   void setSideToolAndAnchor(String id, ToolWindowAnchor anchor, int order, boolean isSide) {
1631     final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1632     setToolWindowAnchor(id, anchor, order);
1633     setSplitModeImpl(id, isSide, commandList);
1634     execute(commandList);
1635   }
1636
1637   private void setSplitModeImpl(final String id, final boolean isSplit, final ArrayList<FinalizableCommand> commandList) {
1638     checkId(id);
1639     final WindowInfoImpl info = getInfo(id);
1640     if (isSplit == info.isSplit()) {
1641       return;
1642     }
1643
1644     myLayout.setSplitMode(id, isSplit);
1645
1646     boolean wasActive = info.isActive();
1647     if (wasActive) {
1648       deactivateToolWindowImpl(id, true, commandList);
1649     }
1650     final WindowInfoImpl[] infos = myLayout.getInfos();
1651     for (WindowInfoImpl info1 : infos) {
1652       appendApplyWindowInfoCmd(info1, commandList);
1653     }
1654     if (wasActive) {
1655       activateToolWindowImpl(id, commandList, true, true);
1656     }
1657     commandList.add(myToolWindowsPane.createUpdateButtonPositionCmd(id, myWindowManager.getCommandProcessor()));
1658   }
1659
1660   ToolWindowType getToolWindowInternalType(final String id) {
1661     ApplicationManager.getApplication().assertIsDispatchThread();
1662     checkId(id);
1663     return getInfo(id).getInternalType();
1664   }
1665
1666   ToolWindowType getToolWindowType(final String id) {
1667     checkId(id);
1668     return getInfo(id).getType();
1669   }
1670
1671   private void fireToolWindowRegistered(final String id) {
1672     myDispatcher.getMulticaster().toolWindowRegistered(id);
1673   }
1674
1675   private void fireStateChanged() {
1676     myDispatcher.getMulticaster().stateChanged();
1677   }
1678
1679   boolean isToolWindowActive(final String id) {
1680     ApplicationManager.getApplication().assertIsDispatchThread();
1681     checkId(id);
1682     return getInfo(id).isActive();
1683   }
1684
1685   boolean isToolWindowAutoHide(final String id) {
1686     ApplicationManager.getApplication().assertIsDispatchThread();
1687     checkId(id);
1688     return getInfo(id).isAutoHide();
1689   }
1690
1691   boolean isToolWindowVisible(final String id) {
1692     checkId(id);
1693     return getInfo(id).isVisible();
1694   }
1695
1696   void setToolWindowAutoHide(final String id, final boolean autoHide) {
1697     ApplicationManager.getApplication().assertIsDispatchThread();
1698     final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1699     setToolWindowAutoHideImpl(id, autoHide, commandList);
1700     execute(commandList);
1701   }
1702
1703   private void setToolWindowAutoHideImpl(final String id, final boolean autoHide, final ArrayList<FinalizableCommand> commandsList) {
1704     checkId(id);
1705     final WindowInfoImpl info = getInfo(id);
1706     if (info.isAutoHide() == autoHide) {
1707       return;
1708     }
1709     info.setAutoHide(autoHide);
1710     appendApplyWindowInfoCmd(info, commandsList);
1711     if (info.isVisible()) {
1712       prepareForActivation(id, commandsList);
1713       showAndActivate(id, false, commandsList, true, true);
1714     }
1715   }
1716
1717   void setToolWindowType(final String id, final ToolWindowType type) {
1718     ApplicationManager.getApplication().assertIsDispatchThread();
1719     final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1720     setToolWindowTypeImpl(id, type, commandList);
1721     execute(commandList);
1722   }
1723
1724   private void setToolWindowTypeImpl(final String id, final ToolWindowType type, final ArrayList<FinalizableCommand> commandsList) {
1725     checkId(id);
1726     final WindowInfoImpl info = getInfo(id);
1727     if (info.getType() == type) {
1728       return;
1729     }
1730     if (info.isVisible()) {
1731       final boolean dirtyMode = info.isDocked() || info.isSliding();
1732       info.setVisible(false);
1733       if (info.isFloating()) {
1734         appendRemoveFloatingDecoratorCmd(info, commandsList);
1735       }
1736       else { // docked and sliding windows
1737         appendRemoveDecoratorCmd(id, dirtyMode, commandsList);
1738       }
1739       info.setType(type);
1740       appendApplyWindowInfoCmd(info, commandsList);
1741       prepareForActivation(id, commandsList);
1742       showAndActivate(id, dirtyMode, commandsList, true, true);
1743       appendUpdateToolWindowsPaneCmd(commandsList);
1744     }
1745     else {
1746       info.setType(type);
1747       appendApplyWindowInfoCmd(info, commandsList);
1748     }
1749   }
1750
1751   private void appendApplyWindowInfoCmd(final WindowInfoImpl info, final List<FinalizableCommand> commandsList) {
1752     final StripeButton button = getStripeButton(info.getId());
1753     final InternalDecorator decorator = getInternalDecorator(info.getId());
1754     commandsList.add(new ApplyWindowInfoCmd(info, button, decorator, myWindowManager.getCommandProcessor()));
1755   }
1756
1757   /**
1758    * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddDecoratorCmd
1759    */
1760   private void appendAddDecoratorCmd(final InternalDecorator decorator,
1761                                      final WindowInfoImpl info,
1762                                      final boolean dirtyMode,
1763                                      final List<FinalizableCommand> commandsList) {
1764     final CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1765     final FinalizableCommand command = myToolWindowsPane.createAddDecoratorCmd(decorator, info, dirtyMode, commandProcessor);
1766     commandsList.add(command);
1767   }
1768
1769   /**
1770    * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createRemoveDecoratorCmd
1771    */
1772   private void appendRemoveDecoratorCmd(final String id, final boolean dirtyMode, final List<FinalizableCommand> commandsList) {
1773     final FinalizableCommand command = myToolWindowsPane.createRemoveDecoratorCmd(id, dirtyMode, myWindowManager.getCommandProcessor());
1774     commandsList.add(command);
1775   }
1776
1777   private void appendRemoveFloatingDecoratorCmd(final WindowInfoImpl info, final List<FinalizableCommand> commandsList) {
1778     final RemoveFloatingDecoratorCmd command = new RemoveFloatingDecoratorCmd(info);
1779     commandsList.add(command);
1780   }
1781
1782   /**
1783    * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddButtonCmd
1784    */
1785   private void appendAddButtonCmd(final StripeButton button, final WindowInfoImpl info, final List<FinalizableCommand> commandsList) {
1786     final Comparator<StripeButton> comparator = myLayout.comparator(info.getAnchor());
1787     final CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1788     final FinalizableCommand command = myToolWindowsPane.createAddButtonCmd(button, info, comparator, commandProcessor);
1789     commandsList.add(command);
1790   }
1791
1792   /**
1793    * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddButtonCmd
1794    */
1795   private void appendRemoveButtonCmd(final String id, final List<FinalizableCommand> commandsList) {
1796     final FinalizableCommand command = myToolWindowsPane.createRemoveButtonCmd(id, myWindowManager.getCommandProcessor());
1797     commandsList.add(command);
1798   }
1799
1800   private ActionCallback appendRequestFocusInEditorComponentCmd(List<FinalizableCommand> commandList, boolean forced) {
1801     if (myProject.isDisposed()) return new ActionCallback.Done();
1802     EditorsSplitters splitters = getSplittersToFocus();
1803     CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1804     RequestFocusInEditorComponentCmd command = new RequestFocusInEditorComponentCmd(splitters, getFocusManager(), commandProcessor, forced);
1805     commandList.add(command);
1806     return command.getDoneCallback();
1807   }
1808
1809   private void appendRequestFocusInToolWindowCmd(final String id, List<FinalizableCommand> commandList, boolean forced) {
1810     final ToolWindowImpl toolWindow = (ToolWindowImpl)getToolWindow(id);
1811     final FocusWatcher focusWatcher = myId2FocusWatcher.get(id);
1812     commandList
1813       .add(new RequestFocusInToolWindowCmd(getFocusManager(), toolWindow, focusWatcher, myWindowManager.getCommandProcessor(), forced));
1814   }
1815
1816   /**
1817    * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createSetEditorComponentCmd
1818    */
1819   public void appendSetEditorComponentCmd(@Nullable final JComponent component, final List<FinalizableCommand> commandsList) {
1820     final CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1821     final FinalizableCommand command = myToolWindowsPane.createSetEditorComponentCmd(component, commandProcessor);
1822     commandsList.add(command);
1823   }
1824
1825   private void appendUpdateToolWindowsPaneCmd(final List<FinalizableCommand> commandsList) {
1826     final JRootPane rootPane = myFrame.getRootPane();
1827     if (rootPane != null) {
1828       final FinalizableCommand command = new UpdateRootPaneCmd(rootPane, myWindowManager.getCommandProcessor());
1829       commandsList.add(command);
1830     }
1831   }
1832
1833   private EditorsSplitters getSplittersToFocus() {
1834     Window activeWindow = myWindowManager.getMostRecentFocusedWindow();
1835
1836     if (activeWindow instanceof FloatingDecorator) {
1837       IdeFocusManager ideFocusManager = IdeFocusManager.findInstanceByComponent(activeWindow);
1838       IdeFrame lastFocusedFrame = ideFocusManager.getLastFocusedFrame();
1839       JComponent frameComponent = lastFocusedFrame != null ? lastFocusedFrame.getComponent() : null;
1840       Window lastFocusedWindow = frameComponent != null ? SwingUtilities.getWindowAncestor(frameComponent) : null;
1841       activeWindow = ObjectUtils.notNull(lastFocusedWindow, activeWindow);
1842     }
1843
1844     FileEditorManagerEx fem = FileEditorManagerEx.getInstanceEx(myProject);
1845     EditorsSplitters splitters = activeWindow != null ? fem.getSplittersFor(activeWindow) : null;
1846     return splitters != null ? splitters : fem.getSplitters();
1847   }
1848
1849   /**
1850    * @return <code>true</code> if tool window with the specified <code>id</code>
1851    * is floating and has modal showing child dialog. Such windows should not be closed
1852    * when auto-hide windows are gone.
1853    */
1854   private boolean hasModalChild(final WindowInfoImpl info) {
1855     if (!info.isVisible() || !info.isFloating()) {
1856       return false;
1857     }
1858     final FloatingDecorator decorator = getFloatingDecorator(info.getId());
1859     LOG.assertTrue(decorator != null);
1860     return isModalOrHasModalChild(decorator);
1861   }
1862
1863   private static boolean isModalOrHasModalChild(final Window window) {
1864     if (window instanceof Dialog) {
1865       final Dialog dialog = (Dialog)window;
1866       if (dialog.isModal() && dialog.isShowing()) {
1867         return true;
1868       }
1869       final Window[] ownedWindows = dialog.getOwnedWindows();
1870       for (int i = ownedWindows.length - 1; i >= 0; i--) {
1871         if (isModalOrHasModalChild(ownedWindows[i])) {
1872           return true;
1873         }
1874       }
1875     }
1876     return false;
1877   }
1878
1879   /**
1880    * Helper method. It deactivates all tool windows excepting the tool window
1881    * which should be activated.
1882    */
1883   private void prepareForActivation(final String id, final List<FinalizableCommand> commandList) {
1884     final WindowInfoImpl toBeActivatedInfo = getInfo(id);
1885     final WindowInfoImpl[] infos = myLayout.getInfos();
1886     for (final WindowInfoImpl info : infos) {
1887       if (id.equals(info.getId())) {
1888         continue;
1889       }
1890       if (toBeActivatedInfo.isDocked() || toBeActivatedInfo.isSliding()) {
1891         deactivateToolWindowImpl(info.getId(), info.isAutoHide() || info.isSliding(), commandList);
1892       }
1893       else { // floating window is being activated
1894         deactivateToolWindowImpl(info.getId(), info.isAutoHide() && info.isFloating() && !hasModalChild(info), commandList);
1895       }
1896     }
1897   }
1898
1899   @Override
1900   public void clearSideStack() {
1901     mySideStack.clear();
1902   }
1903
1904   @Override
1905   public void readExternal(final Element element) {
1906     for (final Object o : element.getChildren()) {
1907       final Element e = (Element)o;
1908       if (EDITOR_ELEMENT.equals(e.getName())) {
1909         myEditorWasActive = Boolean.valueOf(e.getAttributeValue(ACTIVE_ATTR_VALUE)).booleanValue();
1910       }
1911       else if (DesktopLayout.TAG.equals(e.getName())) { // read layout of tool windows
1912         myLayout.readExternal(e);
1913       }
1914       else if (LAYOUT_TO_RESTORE.equals(e.getName())) {
1915         myLayoutToRestoreLater = new DesktopLayout();
1916         myLayoutToRestoreLater.readExternal(e);
1917       }
1918     }
1919   }
1920
1921   @Override
1922   public void writeExternal(final Element element) {
1923     if (myFrame == null) {
1924       // do nothing if the project was not opened
1925       return;
1926     }
1927     final String[] ids = getToolWindowIds();
1928
1929     // Update size of all open floating windows. See SCR #18439
1930     for (final String id : ids) {
1931       final WindowInfoImpl info = getInfo(id);
1932       if (info.isVisible()) {
1933         final InternalDecorator decorator = getInternalDecorator(id);
1934         LOG.assertTrue(decorator != null);
1935         decorator.fireResized();
1936       }
1937     }
1938
1939     // Save frame's bounds
1940     final Rectangle frameBounds = myFrame.getBounds();
1941     final Element frameElement = new Element(FRAME_ELEMENT);
1942     element.addContent(frameElement);
1943     frameElement.setAttribute(X_ATTR, Integer.toString(frameBounds.x));
1944     frameElement.setAttribute(Y_ATTR, Integer.toString(frameBounds.y));
1945     frameElement.setAttribute(WIDTH_ATTR, Integer.toString(frameBounds.width));
1946     frameElement.setAttribute(HEIGHT_ATTR, Integer.toString(frameBounds.height));
1947     frameElement.setAttribute(EXTENDED_STATE_ATTR, Integer.toString(myFrame.getExtendedState()));
1948     // Save whether editor is active or not
1949     final Element editorElement = new Element(EDITOR_ELEMENT);
1950     editorElement.setAttribute(ACTIVE_ATTR_VALUE, isEditorComponentActive() ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
1951     element.addContent(editorElement);
1952     // Save layout of tool windows
1953     final Element layoutElement = new Element(DesktopLayout.TAG);
1954     element.addContent(layoutElement);
1955     myLayout.writeExternal(layoutElement);
1956     if (myLayoutToRestoreLater != null) {
1957       Element layoutToRestoreElement = new Element(LAYOUT_TO_RESTORE);
1958       element.addContent(layoutToRestoreElement);
1959       myLayoutToRestoreLater.writeExternal(layoutToRestoreElement);
1960     }
1961   }
1962
1963   public void setDefaultState(@NotNull final ToolWindowImpl toolWindow,
1964                               @Nullable final ToolWindowAnchor anchor,
1965                               @Nullable final ToolWindowType type,
1966                               @Nullable final Rectangle floatingBounds) {
1967
1968     final WindowInfoImpl info = getInfo(toolWindow.getId());
1969     if (info.wasRead()) return;
1970
1971     if (floatingBounds != null) {
1972       info.setFloatingBounds(floatingBounds);
1973     }
1974
1975     if (anchor != null) {
1976       toolWindow.setAnchor(anchor, null);
1977     }
1978
1979     if (type != null) {
1980       toolWindow.setType(type, null);
1981     }
1982   }
1983
1984   public void setDefaultContentUiType(ToolWindowImpl toolWindow, ToolWindowContentUiType type) {
1985     final WindowInfoImpl info = getInfo(toolWindow.getId());
1986     if (info.wasRead()) return;
1987     toolWindow.setContentUiType(type, null);
1988   }
1989
1990
1991   public void stretchWidth(ToolWindowImpl toolWindow, int value) {
1992     myToolWindowsPane.stretchWidth(toolWindow, value);
1993   }
1994
1995   @Override
1996   public boolean isMaximized(@NotNull ToolWindow wnd) {
1997     return myToolWindowsPane.isMaximized(wnd);
1998   }
1999
2000   @Override
2001   public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) {
2002     myToolWindowsPane.setMaximized(wnd, maximized);
2003   }
2004
2005   public void stretchHeight(ToolWindowImpl toolWindow, int value) {
2006     myToolWindowsPane.stretchHeight(toolWindow, value);
2007   }
2008
2009   private static class BalloonHyperlinkListener implements HyperlinkListener {
2010     private Balloon myBalloon;
2011     private final HyperlinkListener myListener;
2012
2013     public BalloonHyperlinkListener(HyperlinkListener listener) {
2014       myListener = listener;
2015     }
2016
2017     @Override
2018     public void hyperlinkUpdate(HyperlinkEvent e) {
2019       if (myBalloon != null && e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
2020         myBalloon.hide();
2021       }
2022       if (myListener != null) {
2023         myListener.hyperlinkUpdate(e);
2024       }
2025     }
2026   }
2027
2028
2029   /**
2030    * This command creates and shows <code>FloatingDecorator</code>.
2031    */
2032   private final class AddFloatingDecoratorCmd extends FinalizableCommand {
2033     private final FloatingDecorator myFloatingDecorator;
2034
2035     /**
2036      * Creates floating decorator for specified floating decorator.
2037      */
2038     private AddFloatingDecoratorCmd(final InternalDecorator decorator, final WindowInfoImpl info) {
2039       super(myWindowManager.getCommandProcessor());
2040       myFloatingDecorator = new FloatingDecorator(myFrame, info.copy(), decorator);
2041       myId2FloatingDecorator.put(info.getId(), myFloatingDecorator);
2042       final Rectangle bounds = info.getFloatingBounds();
2043       if (bounds != null &&
2044           bounds.width > 0 &&
2045           bounds.height > 0 &&
2046           myWindowManager.isInsideScreenBounds(bounds.x, bounds.y, bounds.width)) {
2047         myFloatingDecorator.setBounds(bounds);
2048       }
2049       else { // place new frame at the center of main frame if there are no floating bounds
2050         Dimension size = decorator.getSize();
2051         if (size.width == 0 || size.height == 0) {
2052           size = decorator.getPreferredSize();
2053         }
2054         myFloatingDecorator.setSize(size);
2055         myFloatingDecorator.setLocationRelativeTo(myFrame);
2056       }
2057     }
2058
2059     @Override
2060     public void run() {
2061       try {
2062         myFloatingDecorator.show();
2063       }
2064       finally {
2065         finish();
2066       }
2067     }
2068   }
2069
2070   /**
2071    * This command hides and destroys floating decorator for tool window
2072    * with specified <code>ID</code>.
2073    */
2074   private final class RemoveFloatingDecoratorCmd extends FinalizableCommand {
2075     private final FloatingDecorator myFloatingDecorator;
2076
2077     private RemoveFloatingDecoratorCmd(final WindowInfoImpl info) {
2078       super(myWindowManager.getCommandProcessor());
2079       myFloatingDecorator = getFloatingDecorator(info.getId());
2080       myId2FloatingDecorator.remove(info.getId());
2081       info.setFloatingBounds(myFloatingDecorator.getBounds());
2082     }
2083
2084     @Override
2085     public void run() {
2086       try {
2087         myFloatingDecorator.dispose();
2088       }
2089       finally {
2090         finish();
2091       }
2092     }
2093
2094     @Override
2095     @Nullable
2096     public Condition getExpireCondition() {
2097       return ApplicationManager.getApplication().getDisposed();
2098     }
2099   }
2100
2101   private final class EditorComponentFocusWatcher extends FocusWatcher {
2102     @Override
2103     protected void focusedComponentChanged(final Component component, final AWTEvent cause) {
2104       if (myWindowManager.getCommandProcessor().getCommandCount() > 0 || component == null) {
2105         return;
2106       }
2107       final KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
2108       final Component owner = mgr.getFocusOwner();
2109
2110       IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new ExpirableRunnable.ForProject(myProject) {
2111         @Override
2112         public void run() {
2113           if (mgr.getFocusOwner() == owner) {
2114             activateEditorComponent(false);
2115           }
2116         }
2117       });
2118     }
2119   }
2120
2121
2122   /**
2123    * Notifies window manager about focus traversal in tool window
2124    */
2125   private final class ToolWindowFocusWatcher extends FocusWatcher {
2126     private final String myId;
2127     private final ToolWindowImpl myToolWindow;
2128
2129
2130     private ToolWindowFocusWatcher(final ToolWindowImpl toolWindow) {
2131       myId = toolWindow.getId();
2132       install(toolWindow.getComponent());
2133       myToolWindow = toolWindow;
2134     }
2135
2136     public void deinstall() {
2137       deinstall(myToolWindow.getComponent());
2138     }
2139
2140     @Override
2141     protected boolean isFocusedComponentChangeValid(final Component comp, final AWTEvent cause) {
2142       return myWindowManager.getCommandProcessor().getCommandCount() == 0 && comp != null;
2143     }
2144
2145     @Override
2146     protected void focusedComponentChanged(final Component component, final AWTEvent cause) {
2147       if (myWindowManager.getCommandProcessor().getCommandCount() > 0 || component == null) {
2148         return;
2149       }
2150       final WindowInfoImpl info = getInfo(myId);
2151       //getFocusManagerImpl(myProject)..cancelAllRequests();
2152
2153       if (!info.isActive()) {
2154         getFocusManagerImpl(myProject).doWhenFocusSettlesDown(new EdtRunnable() {
2155           @Override
2156           public void runEdt() {
2157             if (!myLayout.isToolWindowRegistered(myId)) return;
2158             activateToolWindow(myId, false, false);
2159           }
2160         });
2161       }
2162     }
2163   }
2164
2165   /**
2166    * Spies on IdeToolWindow properties and applies them to the window
2167    * state.
2168    */
2169   private final class MyToolWindowPropertyChangeListener implements PropertyChangeListener {
2170     @Override
2171     public void propertyChange(final PropertyChangeEvent e) {
2172       ToolWindowImpl toolWindow = (ToolWindowImpl)e.getSource();
2173       if (ToolWindowEx.PROP_AVAILABLE.equals(e.getPropertyName())) {
2174         final WindowInfoImpl info = getInfo(toolWindow.getId());
2175         if (!toolWindow.isAvailable() && info.isVisible()) {
2176           hideToolWindow(toolWindow.getId(), false);
2177         }
2178       }
2179       StripeButton button = myId2StripeButton.get(toolWindow.getId());
2180       if (button != null) button.updatePresentation();
2181       ActivateToolWindowAction.updateToolWindowActionPresentation(toolWindow);
2182     }
2183   }
2184
2185   /**
2186    * Translates events from InternalDecorator into ToolWindowManager method invocations.
2187    */
2188   private final class MyInternalDecoratorListener implements InternalDecoratorListener {
2189     @Override
2190     public void anchorChanged(final InternalDecorator source, final ToolWindowAnchor anchor) {
2191       setToolWindowAnchor(source.getToolWindow().getId(), anchor);
2192     }
2193
2194     @Override
2195     public void autoHideChanged(final InternalDecorator source, final boolean autoHide) {
2196       setToolWindowAutoHide(source.getToolWindow().getId(), autoHide);
2197     }
2198
2199     @Override
2200     public void hidden(final InternalDecorator source) {
2201       hideToolWindow(source.getToolWindow().getId(), false);
2202     }
2203
2204     @Override
2205     public void hiddenSide(final InternalDecorator source) {
2206       hideToolWindow(source.getToolWindow().getId(), true);
2207     }
2208
2209     @Override
2210     public void contentUiTypeChanges(InternalDecorator source, ToolWindowContentUiType type) {
2211       setContentUiType(source.getToolWindow().getId(), type);
2212     }
2213
2214     /**
2215      * Handles event from decorator and modify weight/floating bounds of the
2216      * tool window depending on decoration type.
2217      */
2218     @Override
2219     public void resized(final InternalDecorator source) {
2220       if (!source.isShowing()) {
2221         return; // do not recalculate the tool window size if it is not yet shown (and, therefore, has 0,0,0,0 bounds)
2222       }
2223
2224       final WindowInfoImpl info = getInfo(source.getToolWindow().getId());
2225       InternalDecorator another = null;
2226       if (info.isFloating()) {
2227         final Window owner = SwingUtilities.getWindowAncestor(source);
2228         if (owner != null) {
2229           info.setFloatingBounds(owner.getBounds());
2230         }
2231       }
2232       else { // docked and sliding windows
2233         ToolWindowAnchor anchor = info.getAnchor();
2234         if (source.getParent() instanceof Splitter) {
2235           float sizeInSplit = anchor.isSplitVertically() ? source.getHeight() : source.getWidth();
2236           Splitter splitter = (Splitter)source.getParent();
2237           if (splitter.getSecondComponent() == source) {
2238             sizeInSplit += splitter.getDividerWidth();
2239             another = (InternalDecorator)splitter.getFirstComponent();
2240           }
2241           else {
2242             another = (InternalDecorator)splitter.getSecondComponent();
2243           }
2244           if (anchor.isSplitVertically()) {
2245             info.setSideWeight(sizeInSplit / (float)splitter.getHeight());
2246           }
2247           else {
2248             info.setSideWeight(sizeInSplit / (float)splitter.getWidth());
2249           }
2250         }
2251
2252         float paneWeight = anchor.isHorizontal()
2253                            ? (float)source.getHeight() / (float)myToolWindowsPane.getMyLayeredPane().getHeight()
2254                            : (float)source.getWidth() / (float)myToolWindowsPane.getMyLayeredPane().getWidth();
2255         info.setWeight(paneWeight);
2256         if (another != null && anchor.isSplitVertically()) {
2257           paneWeight = anchor.isHorizontal()
2258                        ? (float)another.getHeight() / (float)myToolWindowsPane.getMyLayeredPane().getHeight()
2259                        : (float)another.getWidth() / (float)myToolWindowsPane.getMyLayeredPane().getWidth();
2260           another.getWindowInfo().setWeight(paneWeight);
2261         }
2262       }
2263     }
2264
2265     @Override
2266     public void activated(final InternalDecorator source) {
2267       activateToolWindow(source.getToolWindow().getId(), true, true);
2268     }
2269
2270     @Override
2271     public void typeChanged(final InternalDecorator source, final ToolWindowType type) {
2272       setToolWindowType(source.getToolWindow().getId(), type);
2273     }
2274
2275     @Override
2276     public void sideStatusChanged(final InternalDecorator source, final boolean isSideTool) {
2277       setSideTool(source.getToolWindow().getId(), isSideTool);
2278     }
2279   }
2280
2281   private void updateComponentTreeUI() {
2282     ApplicationManager.getApplication().assertIsDispatchThread();
2283     final WindowInfoImpl[] infos = myLayout.getInfos();
2284     for (WindowInfoImpl info : infos) {
2285       // the main goal is to update hidden TW components because they are not in the hierarchy
2286       // and will not be updated automatically but unfortunately the visibility of a TW may change
2287       // during the same actionPerformed() so we can't optimize and have to process all of them
2288       IJSwingUtilities.updateComponentTreeUI(getInternalDecorator(info.getId()));
2289     }
2290   }
2291
2292   private final class MyUIManagerPropertyChangeListener implements PropertyChangeListener {
2293     @Override
2294     public void propertyChange(final PropertyChangeEvent e) {
2295       updateComponentTreeUI();
2296     }
2297   }
2298
2299   private final class MyLafManagerListener implements LafManagerListener {
2300     @Override
2301     public void lookAndFeelChanged(final LafManager source) {
2302       updateComponentTreeUI();
2303     }
2304   }
2305
2306
2307   @Override
2308   @NotNull
2309   public String getComponentName() {
2310     return "ToolWindowManager";
2311   }
2312
2313   @NotNull
2314   public ActionCallback requestDefaultFocus(final boolean forced) {
2315     return getFocusManagerImpl(myProject).requestFocus(new FocusCommand() {
2316       @NotNull
2317       @Override
2318       public ActionCallback run() {
2319         return processDefaultFocusRequest(forced);
2320       }
2321     }, forced);
2322   }
2323
2324   private void focusToolWinowByDefault(@Nullable String idToIngore) {
2325     String toFocus = null;
2326
2327     for (String each : myActiveStack.getStack()) {
2328       if (idToIngore != null && idToIngore.equalsIgnoreCase(each)) continue;
2329
2330       if (getInfo(each).isVisible()) {
2331         toFocus = each;
2332         break;
2333       }
2334     }
2335
2336     if (toFocus == null) {
2337       for (String each : myActiveStack.getPersistentStack()) {
2338         if (idToIngore != null && idToIngore.equalsIgnoreCase(each)) continue;
2339
2340         if (getInfo(each).isVisible()) {
2341           toFocus = each;
2342           break;
2343         }
2344       }
2345     }
2346
2347     if (toFocus != null) {
2348       activateToolWindow(toFocus, false, true);
2349     }
2350   }
2351
2352   private ActionCallback processDefaultFocusRequest(boolean forced) {
2353     if (ModalityState.NON_MODAL.equals(ModalityState.current())) {
2354       final String activeId = getActiveToolWindowId();
2355       if (isEditorComponentActive() || activeId == null || getToolWindow(activeId) == null) {
2356         activateEditorComponent(forced, true);
2357       }
2358       else {
2359         activateToolWindow(activeId, forced, true);
2360       }
2361
2362       return new ActionCallback.Done();
2363     }
2364     Window activeWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
2365     if (activeWindow != null) {
2366       JRootPane root = null;
2367       if (activeWindow instanceof JDialog) {
2368         root = ((JDialog)activeWindow).getRootPane();
2369       }
2370       else if (activeWindow instanceof JFrame) {
2371         root = ((JFrame)activeWindow).getRootPane();
2372       }
2373
2374       if (root != null) {
2375         JComponent toFocus = IdeFocusTraversalPolicy.getPreferredFocusedComponent(root);
2376         if (toFocus != null) {
2377           if (DialogWrapper.findInstance(toFocus) != null) {
2378             return new ActionCallback.Done(); //IDEA-80929
2379           }
2380           return IdeFocusManager.findInstanceByComponent(toFocus).requestFocus(toFocus, forced);
2381         }
2382       }
2383     }
2384     return new ActionCallback.Rejected();
2385   }
2386
2387
2388   /**
2389    * Delegate method for compatibility with older versions of IDEA
2390    */
2391   @NotNull
2392   public ActionCallback requestFocus(@NotNull Component c, boolean forced) {
2393     return IdeFocusManager.getInstance(myProject).requestFocus(c, forced);
2394   }
2395
2396   @NotNull
2397   public ActionCallback requestFocus(@NotNull FocusCommand command, boolean forced) {
2398     return IdeFocusManager.getInstance(myProject).requestFocus(command, forced);
2399   }
2400
2401   public void doWhenFocusSettlesDown(@NotNull Runnable runnable) {
2402     IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(runnable);
2403   }
2404
2405   public boolean dispatch(@NotNull KeyEvent e) {
2406     return IdeFocusManager.getInstance(myProject).dispatch(e);
2407   }
2408
2409   public Expirable getTimestamp(boolean trackOnlyForcedCommands) {
2410     return IdeFocusManager.getInstance(myProject).getTimestamp(trackOnlyForcedCommands);
2411   }
2412 }