2 * Copyright 2000-2015 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.openapi.wm.impl;
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.internal.statistic.UsageTrigger;
24 import com.intellij.openapi.Disposable;
25 import com.intellij.openapi.actionSystem.*;
26 import com.intellij.openapi.actionSystem.ex.AnActionListener;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.application.ModalityState;
29 import com.intellij.openapi.components.ProjectComponent;
30 import com.intellij.openapi.diagnostic.Logger;
31 import com.intellij.openapi.editor.impl.EditorComponentImpl;
32 import com.intellij.openapi.extensions.Extensions;
33 import com.intellij.openapi.fileEditor.FileEditorManager;
34 import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
35 import com.intellij.openapi.fileEditor.FileEditorManagerListener;
36 import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
37 import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
38 import com.intellij.openapi.keymap.Keymap;
39 import com.intellij.openapi.keymap.KeymapManager;
40 import com.intellij.openapi.progress.ProcessCanceledException;
41 import com.intellij.openapi.progress.ProgressIndicator;
42 import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
43 import com.intellij.openapi.progress.util.ReadTask;
44 import com.intellij.openapi.project.DumbAwareRunnable;
45 import com.intellij.openapi.project.DumbService;
46 import com.intellij.openapi.project.Project;
47 import com.intellij.openapi.startup.StartupManager;
48 import com.intellij.openapi.ui.DialogWrapper;
49 import com.intellij.openapi.ui.MessageType;
50 import com.intellij.openapi.ui.Splitter;
51 import com.intellij.openapi.ui.popup.Balloon;
52 import com.intellij.openapi.ui.popup.JBPopupFactory;
53 import com.intellij.openapi.util.*;
54 import com.intellij.openapi.util.registry.Registry;
55 import com.intellij.openapi.vfs.VirtualFile;
56 import com.intellij.openapi.wm.*;
57 import com.intellij.openapi.wm.ex.*;
58 import com.intellij.openapi.wm.impl.commands.*;
59 import com.intellij.ui.BalloonImpl;
60 import com.intellij.ui.ColorUtil;
61 import com.intellij.ui.awt.RelativePoint;
62 import com.intellij.ui.switcher.QuickAccessSettings;
63 import com.intellij.ui.switcher.SwitchManager;
64 import com.intellij.util.*;
65 import com.intellij.util.containers.ContainerUtil;
66 import com.intellij.util.containers.HashMap;
67 import com.intellij.util.ui.PositionTracker;
68 import com.intellij.util.ui.UIUtil;
69 import com.intellij.util.ui.update.UiNotifyConnector;
70 import org.jdom.Element;
71 import org.jetbrains.annotations.NonNls;
72 import org.jetbrains.annotations.NotNull;
73 import org.jetbrains.annotations.Nullable;
76 import javax.swing.event.HyperlinkEvent;
77 import javax.swing.event.HyperlinkListener;
79 import java.awt.event.FocusEvent;
80 import java.awt.event.InputEvent;
81 import java.awt.event.KeyEvent;
82 import java.awt.event.WindowEvent;
83 import java.beans.PropertyChangeEvent;
84 import java.beans.PropertyChangeListener;
86 import java.util.List;
88 import static com.intellij.openapi.wm.impl.FloatingDecorator.DIVIDER_WIDTH;
91 * @author Anton Katilin
92 * @author Vladimir Kondratyev
94 public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements ProjectComponent, JDOMExternalizable {
95 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.ToolWindowManagerImpl");
97 private final Project myProject;
98 private final WindowManagerEx myWindowManager;
99 private final EventDispatcher<ToolWindowManagerListener> myDispatcher = EventDispatcher.create(ToolWindowManagerListener.class);
100 private final DesktopLayout myLayout;
101 private final Map<String, InternalDecorator> myId2InternalDecorator;
102 private final Map<String, FloatingDecorator> myId2FloatingDecorator;
103 private final Map<String, WindowedDecorator> myId2WindowedDecorator;
104 private final Map<String, StripeButton> myId2StripeButton;
105 private final Map<String, FocusWatcher> myId2FocusWatcher;
106 private final Set<String> myDumbAwareIds = Collections.synchronizedSet(ContainerUtil.<String>newTroveSet());
108 private final EditorComponentFocusWatcher myEditorComponentFocusWatcher;
109 private final MyToolWindowPropertyChangeListener myToolWindowPropertyChangeListener;
110 private final InternalDecoratorListener myInternalDecoratorListener;
112 private boolean myEditorWasActive;
114 private final ActiveStack myActiveStack;
115 private final SideStack mySideStack;
117 private ToolWindowsPane myToolWindowsPane;
118 private IdeFrameImpl myFrame;
119 private DesktopLayout myLayoutToRestoreLater = null;
120 @NonNls private static final String EDITOR_ELEMENT = "editor";
121 @NonNls private static final String ACTIVE_ATTR_VALUE = "active";
122 @NonNls private static final String FRAME_ELEMENT = "frame";
123 @NonNls private static final String X_ATTR = "x";
124 @NonNls private static final String Y_ATTR = "y";
125 @NonNls private static final String WIDTH_ATTR = "width";
126 @NonNls private static final String HEIGHT_ATTR = "height";
127 @NonNls private static final String EXTENDED_STATE_ATTR = "extended-state";
128 @NonNls private static final String LAYOUT_TO_RESTORE = "layout-to-restore";
130 private final FileEditorManager myFileEditorManager;
131 private final LafManager myLafManager;
132 private final Map<String, Balloon> myWindow2Balloon = new HashMap<String, Balloon>();
133 private Pair<String, Integer> myMaximizedToolwindowSize = null;
135 private KeyState myCurrentState = KeyState.waiting;
136 private final Alarm myWaiterForSecondPress = new Alarm();
137 private final Runnable mySecondPressRunnable = new Runnable() {
140 if (myCurrentState != KeyState.hold) {
145 private final PropertyChangeListener myFocusListener;
147 public boolean isToolWindowRegistered(String id) {
148 return myLayout.isToolWindowRegistered(id);
151 private enum KeyState {
152 waiting, pressed, released, hold
155 private final Alarm myUpdateHeadersAlarm = new Alarm();
156 private final Runnable myUpdateHeadersRunnable = new Runnable() {
159 updateToolWindowHeaders();
164 * invoked by reflection
166 public ToolWindowManagerImpl(final Project project,
167 final WindowManagerEx windowManagerEx,
168 final FileEditorManager fem,
169 final ActionManager actionManager,
170 final LafManager lafManager) {
172 myWindowManager = windowManagerEx;
173 myFileEditorManager = fem;
174 myLafManager = lafManager;
176 if (!project.isDefault()) {
177 actionManager.addAnActionListener(new AnActionListener() {
179 public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
180 if (myCurrentState != KeyState.hold) {
186 public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
190 public void beforeEditorTyping(char c, DataContext dataContext) {
195 myLayout = new DesktopLayout();
196 myLayout.copyFrom(windowManagerEx.getLayout());
198 myId2InternalDecorator = new HashMap<String, InternalDecorator>();
199 myId2FloatingDecorator = new HashMap<String, FloatingDecorator>();
200 myId2WindowedDecorator = new HashMap<String, WindowedDecorator>();
201 myId2StripeButton = new HashMap<String, StripeButton>();
202 myId2FocusWatcher = new HashMap<String, FocusWatcher>();
204 myEditorComponentFocusWatcher = new EditorComponentFocusWatcher();
205 myToolWindowPropertyChangeListener = new MyToolWindowPropertyChangeListener();
206 myInternalDecoratorListener = new MyInternalDecoratorListener();
208 myActiveStack = new ActiveStack();
209 mySideStack = new SideStack();
211 project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerListener() {
213 public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
217 public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
218 getFocusManagerImpl(myProject).doWhenFocusSettlesDown(new ExpirableRunnable.ForProject(myProject) {
221 if (!hasOpenEditorFiles()) {
222 focusToolWinowByDefault(null);
229 public void selectionChanged(@NotNull FileEditorManagerEvent event) {
233 myFocusListener = new PropertyChangeListener() {
235 public void propertyChange(PropertyChangeEvent evt) {
236 if ("focusOwner".equals(evt.getPropertyName())) {
237 myUpdateHeadersAlarm.cancelAllRequests();
238 myUpdateHeadersAlarm.addRequest(myUpdateHeadersRunnable, 50);
242 KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(myFocusListener);
246 private void updateToolWindowHeaders() {
247 getFocusManager().doWhenFocusSettlesDown(new ExpirableRunnable.ForProject(myProject) {
250 WindowInfoImpl[] infos = myLayout.getInfos();
251 for (WindowInfoImpl each : infos) {
252 if (each.isVisible()) {
253 ToolWindow tw = getToolWindow(each.getId());
254 if (tw instanceof ToolWindowImpl) {
255 InternalDecorator decorator = ((ToolWindowImpl)tw).getDecorator();
256 if (decorator != null) {
266 public boolean dispatchKeyEvent(KeyEvent e) {
267 if (e.getKeyCode() != KeyEvent.VK_CONTROL &&
268 e.getKeyCode() != KeyEvent.VK_ALT &&
269 e.getKeyCode() != KeyEvent.VK_SHIFT &&
270 e.getKeyCode() != KeyEvent.VK_META) {
271 if (e.getModifiers() == 0) {
276 if (e.getID() != KeyEvent.KEY_PRESSED && e.getID() != KeyEvent.KEY_RELEASED) return false;
278 Component parent = UIUtil.findUltimateParent(e.getComponent());
279 if (parent instanceof IdeFrame) {
280 if (((IdeFrame)parent).getProject() != myProject) {
286 Set<Integer> vks = getActivateToolWindowVKs();
293 if (vks.contains(e.getKeyCode())) {
294 boolean pressed = e.getID() == KeyEvent.KEY_PRESSED;
295 int modifiers = e.getModifiers();
297 int mouseMask = InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK | InputEvent.BUTTON3_DOWN_MASK;
298 if ((e.getModifiersEx() & mouseMask) == 0) {
299 if (SwitchManager.areAllModifiersPressed(modifiers, vks) || !pressed) {
300 processState(pressed);
312 public static Set<Integer> getActivateToolWindowVKs() {
313 if (ApplicationManager.getApplication() == null) return new HashSet<Integer>();
315 Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
316 Shortcut[] baseShortcut = keymap.getShortcuts("ActivateProjectToolWindow");
317 int baseModifiers = 0;
318 for (Shortcut each : baseShortcut) {
319 if (each instanceof KeyboardShortcut) {
320 KeyStroke keyStroke = ((KeyboardShortcut)each).getFirstKeyStroke();
321 baseModifiers = keyStroke.getModifiers();
322 if (baseModifiers > 0) {
327 return QuickAccessSettings.getModifiersVKs(baseModifiers);
330 private void resetHoldState() {
331 myCurrentState = KeyState.waiting;
335 private void processState(boolean pressed) {
337 if (myCurrentState == KeyState.waiting) {
338 myCurrentState = KeyState.pressed;
340 else if (myCurrentState == KeyState.released) {
341 myCurrentState = KeyState.hold;
346 if (myCurrentState == KeyState.pressed) {
347 myCurrentState = KeyState.released;
348 restartWaitingForSecondPressAlarm();
356 private void processHoldState() {
357 if (myToolWindowsPane != null) {
358 myToolWindowsPane.setStripesOverlayed(myCurrentState == KeyState.hold);
362 private void restartWaitingForSecondPressAlarm() {
363 myWaiterForSecondPress.cancelAllRequests();
364 myWaiterForSecondPress.addRequest(mySecondPressRunnable, Registry.intValue("actionSystem.keyGestureDblClickTime"));
367 private boolean hasOpenEditorFiles() {
368 return myFileEditorManager.getOpenFiles().length > 0;
371 private static IdeFocusManager getFocusManagerImpl(Project project) {
372 return IdeFocusManager.getInstance(project);
376 public Project getProject() {
381 public void initComponent() {
385 public void disposeComponent() {
386 for (String id : new ArrayList<String>(myId2StripeButton.keySet())) {
387 unregisterToolWindow(id);
390 assert myId2StripeButton.isEmpty();
392 KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener(myFocusListener);
396 public void projectOpened() {
397 final MyUIManagerPropertyChangeListener uiManagerPropertyListener = new MyUIManagerPropertyChangeListener();
398 final MyLafManagerListener lafManagerListener = new MyLafManagerListener();
400 UIManager.addPropertyChangeListener(uiManagerPropertyListener);
401 myLafManager.addLafManagerListener(lafManagerListener);
403 Disposer.register(myProject, new Disposable() {
405 public void dispose() {
406 UIManager.removePropertyChangeListener(uiManagerPropertyListener);
407 myLafManager.removeLafManagerListener(lafManagerListener);
410 myFrame = myWindowManager.allocateFrame(myProject);
411 LOG.assertTrue(myFrame != null);
413 final ArrayList<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
415 myToolWindowsPane = new ToolWindowsPane(myFrame, this);
416 Disposer.register(myProject, myToolWindowsPane);
417 ((IdeRootPane)myFrame.getRootPane()).setToolWindowsPane(myToolWindowsPane);
418 appendUpdateToolWindowsPaneCmd(commandsList);
420 myFrame.setTitle(FrameTitleBuilder.getInstance().getProjectTitle(myProject));
421 JComponent editorComponent = createEditorComponent(myProject);
422 myEditorComponentFocusWatcher.install(editorComponent);
424 appendSetEditorComponentCmd(editorComponent, commandsList);
425 if (myEditorWasActive && editorComponent instanceof EditorsSplitters) {
426 activateEditorComponentImpl(commandsList, true);
428 execute(commandsList);
430 final DumbService.DumbModeListener dumbModeListener = new DumbService.DumbModeListener() {
432 public void enteredDumbMode() {
433 disableStripeButtons();
437 public void exitDumbMode() {
438 for (final String id : getToolWindowIds()) {
439 getStripeButton(id).setEnabled(true);
443 myProject.getMessageBus().connect().subscribe(DumbService.DUMB_MODE, dumbModeListener);
445 StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() {
448 registerToolWindowsFromBeans();
449 if (DumbService.getInstance(myProject).isDumb()) {
450 disableStripeButtons();
455 IdeEventQueue.getInstance().addDispatcher(new IdeEventQueue.EventDispatcher() {
457 public boolean dispatch(AWTEvent e) {
458 if (e instanceof KeyEvent) {
459 dispatchKeyEvent((KeyEvent)e);
461 if (e instanceof WindowEvent && (e.getID() == WindowEvent.WINDOW_LOST_FOCUS) && e.getSource() == myFrame) {
469 private void disableStripeButtons() {
470 for (final String id : getToolWindowIds()) {
471 if (!myDumbAwareIds.contains(id)) {
472 if (isToolWindowVisible(id)) {
473 hideToolWindow(id, true);
475 StripeButton button = getStripeButton(id);
476 if (button != null) {
477 button.setEnabled(false);
483 private JComponent createEditorComponent(Project project) {
484 return FrameEditorComponentProvider.EP.getExtensions()[0].createEditorComponent(project);
487 private void registerToolWindowsFromBeans() {
488 ToolWindowEP[] beans = Extensions.getExtensions(ToolWindowEP.EP_NAME);
489 for (final ToolWindowEP bean : beans) {
490 final Condition<Project> condition = bean.getCondition();
491 if (condition == null) {
492 initToolWindow(bean);
495 checkConditionInReadAction(bean, condition);
500 private void checkConditionInReadAction(@NotNull final ToolWindowEP bean, @NotNull final Condition<Project> condition) {
501 ProgressIndicatorUtils.scheduleWithWriteActionPriority(new ReadTask() {
503 public void computeInReadAction(@NotNull ProgressIndicator indicator) throws ProcessCanceledException {
504 if (!myProject.isDisposed() && condition.value(myProject)) {
505 ApplicationManager.getApplication().invokeLater(new Runnable() {
508 if (!myProject.isDisposed() && getToolWindow(bean.id) == null) {
509 initToolWindow(bean);
517 public void onCanceled(@NotNull ProgressIndicator indicator) {
518 checkConditionInReadAction(bean, condition);
524 public void initToolWindow(@NotNull ToolWindowEP bean) {
525 JLabel label = new JLabel("Initializing...", SwingConstants.CENTER);
526 label.setOpaque(true);
527 final Color treeBg = UIManager.getColor("Tree.background");
528 label.setBackground(ColorUtil.toAlpha(treeBg, 180));
529 final Color treeFg = UIUtil.getTreeForeground();
530 label.setForeground(ColorUtil.toAlpha(treeFg, 180));
531 ToolWindowAnchor toolWindowAnchor = ToolWindowAnchor.fromText(bean.anchor);
532 final ToolWindowFactory factory = bean.getToolWindowFactory();
533 final ToolWindowImpl toolWindow =
534 (ToolWindowImpl)registerToolWindow(bean.id, label, toolWindowAnchor, myProject, DumbService.isDumbAware(factory),
535 bean.canCloseContents);
536 toolWindow.setContentFactory(factory);
537 if (bean.icon != null && toolWindow.getIcon() == null) {
538 Icon icon = IconLoader.findIcon(bean.icon, factory.getClass());
541 icon = IconLoader.getIcon(bean.icon);
543 catch (Exception ignored) {
546 toolWindow.setIcon(icon);
549 WindowInfoImpl info = getInfo(bean.id);
550 if (!info.isSplit() && bean.secondary && !info.wasRead()) {
551 toolWindow.setSplitMode(bean.secondary, null);
554 final ActionCallback activation = toolWindow.setActivation(new ActionCallback());
556 final DumbAwareRunnable runnable = new DumbAwareRunnable() {
559 if (toolWindow.isDisposed()) return;
561 toolWindow.ensureContentInitialized();
562 activation.setDone();
565 if (ApplicationManager.getApplication().isUnitTestMode()) {
569 UiNotifyConnector.doWhenFirstShown(label, new Runnable() {
572 ApplicationManager.getApplication().invokeLater(runnable);
579 public void projectClosed() {
580 final ArrayList<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
581 final String[] ids = getToolWindowIds();
583 // Remove ToolWindowsPane
584 if (myFrame != null) {
585 ((IdeRootPane)myFrame.getRootPane()).setToolWindowsPane(null);
586 myWindowManager.releaseFrame(myFrame);
588 appendUpdateToolWindowsPaneCmd(commandsList);
590 // Hide all tool windows
592 for (final String id : ids) {
593 deactivateToolWindowImpl(id, true, commandsList);
596 // Remove editor component
598 final JComponent editorComponent = FileEditorManagerEx.getInstanceEx(myProject).getComponent();
599 myEditorComponentFocusWatcher.deinstall(editorComponent);
600 appendSetEditorComponentCmd(null, commandsList);
601 execute(commandsList);
605 public void addToolWindowManagerListener(@NotNull ToolWindowManagerListener l) {
606 myDispatcher.addListener(l);
610 public void addToolWindowManagerListener(@NotNull ToolWindowManagerListener l, @NotNull Disposable parentDisposable) {
611 myDispatcher.addListener(l, parentDisposable);
615 public void removeToolWindowManagerListener(@NotNull ToolWindowManagerListener l) {
616 myDispatcher.removeListener(l);
620 * This is helper method. It delegated its functionality to the WindowManager.
621 * Before delegating it fires state changed.
623 public void execute(@NotNull List<FinalizableCommand> commandList) {
624 for (FinalizableCommand each : commandList) {
625 if (each.willChangeState()) {
631 for (FinalizableCommand each : commandList) {
632 each.beforeExecute(this);
634 myWindowManager.getCommandProcessor().execute(commandList, myProject.getDisposed());
638 public void activateEditorComponent() {
639 activateEditorComponent(true);
642 private void activateEditorComponent(final boolean forced) {
643 activateEditorComponent(forced, false); //TODO[kirillk]: runnable in activateEditorComponent(boolean, boolean) never runs
646 private void activateEditorComponent(final boolean forced, boolean now) {
647 if (LOG.isDebugEnabled()) {
648 LOG.debug("enter: activateEditorComponent()");
650 ApplicationManager.getApplication().assertIsDispatchThread();
652 final ExpirableRunnable runnable = new ExpirableRunnable.ForProject(myProject) {
655 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
656 activateEditorComponentImpl(commandList, forced);
657 execute(commandList);
661 if (!runnable.isExpired()) {
666 final FocusRequestor requestor = getFocusManager().getFurtherRequestor();
667 getFocusManager().doWhenFocusSettlesDown(new ExpirableRunnable.ForProject(myProject) {
670 requestor.requestFocus(new FocusCommand() {
673 public ActionCallback run() {
675 return ActionCallback.DONE;
677 }.setExpirable(runnable), forced);
683 private void activateEditorComponentImpl(List<FinalizableCommand> commandList, final boolean forced) {
684 final String active = getActiveToolWindowId();
685 // Now we have to request focus into most recent focused editor
686 appendRequestFocusInEditorComponentCmd(commandList, forced).doWhenDone(new Runnable() {
689 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
691 if (LOG.isDebugEnabled()) {
692 LOG.debug("editor activated");
694 deactivateWindows(null, commandList);
695 myActiveStack.clear();
697 execute(commandList);
699 }).doWhenRejected(new Runnable() {
703 getFocusManagerImpl(myProject).requestFocus(new FocusCommand() {
706 public ActionCallback run() {
707 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
709 final WindowInfoImpl toReactivate = getInfo(active);
710 final boolean reactivateLastActive = toReactivate != null && !isToHideOnDeactivation(toReactivate);
711 deactivateWindows(reactivateLastActive ? active : null, commandList);
712 execute(commandList);
714 if (reactivateLastActive) {
715 activateToolWindow(active, false, true);
718 if (active != null) {
719 myActiveStack.remove(active, false);
722 if (!myActiveStack.isEmpty()) {
723 activateToolWindow(myActiveStack.peek(), false, true);
726 return ActionCallback.DONE;
734 private void deactivateWindows( @Nullable String idToIgnore, final List<FinalizableCommand> commandList) {
735 final WindowInfoImpl[] infos = myLayout.getInfos();
736 for (final WindowInfoImpl info : infos) {
737 if (idToIgnore != null && idToIgnore.equals(info.getId())) {
740 deactivateToolWindowImpl(info.getId(), isToHideOnDeactivation(info), commandList);
744 private static boolean isToHideOnDeactivation(@NotNull final WindowInfoImpl info) {
745 if (info.isFloating() || info.isWindowed()) return false;
746 return info.isAutoHide() || info.isSliding();
750 * Helper method. It makes window visible, activates it and request focus into the tool window.
751 * But it doesn't deactivate other tool windows. Use <code>prepareForActivation</code> method to
752 * deactivates other tool windows.
754 * @param dirtyMode if <code>true</code> then all UI operations are performed in "dirty" mode.
755 * It means that UI isn't validated and repainted just after each add/remove operation.
756 * @see ToolWindowManagerImpl#prepareForActivation
758 private void showAndActivate(final String id,
759 final boolean dirtyMode,
760 List<FinalizableCommand> commandsList,
761 boolean autoFocusContents,
762 boolean forcedFocusRequest) {
763 if (!getToolWindow(id).isAvailable()) {
767 final WindowInfoImpl info = getInfo(id);
768 boolean toApplyInfo = false;
769 if (!info.isActive()) {
770 info.setActive(true);
773 showToolWindowImpl(id, dirtyMode, commandsList);
777 appendApplyWindowInfoCmd(info, commandsList);
778 myActiveStack.push(id);
781 if (autoFocusContents && ApplicationManager.getApplication().isActive()) {
782 appendRequestFocusInToolWindowCmd(id, commandsList, forcedFocusRequest);
786 void activateToolWindow(final String id, boolean forced, boolean autoFocusContents) {
787 if (LOG.isDebugEnabled()) {
788 LOG.debug("enter: activateToolWindow(" + id + ")");
790 ApplicationManager.getApplication().assertIsDispatchThread();
792 if (DumbService.getInstance(myProject).isDumb() && !myDumbAwareIds.contains(id)) {
796 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
797 activateToolWindowImpl(id, commandList, forced, autoFocusContents);
798 execute(commandList);
801 private void activateToolWindowImpl(final String id,
802 List<FinalizableCommand> commandList,
804 boolean autoFocusContents) {
805 if (!FocusManagerImpl.getInstance().isUnforcedRequestAllowed() && !forced) return;
807 if (LOG.isDebugEnabled()) {
808 LOG.debug("enter: activateToolWindowImpl(" + id + ")");
810 if (!getToolWindow(id).isAvailable()) {
811 // Tool window can be "logically" active but not focused. For example,
812 // when the user switched to another application. So we just need to bring
813 // tool window's window to front.
814 final InternalDecorator decorator = getInternalDecorator(id);
815 if (!decorator.hasFocus() && autoFocusContents) {
816 appendRequestFocusInToolWindowCmd(id, commandList, forced);
820 deactivateWindows(id, commandList);
821 showAndActivate(id, false, commandList, autoFocusContents, forced);
825 * Checkes whether the specified <code>id</code> defines installed tool
826 * window. If it's not then throws <code>IllegalStateException</code>.
828 * @throws IllegalStateException if tool window isn't installed.
830 private void checkId(final String id) {
831 if (!myLayout.isToolWindowRegistered(id)) {
832 throw new IllegalStateException("window with id=\"" + id + "\" isn't registered");
837 * Helper method. It deactivates (and hides) window with specified <code>id</code>.
839 * @param id <code>id</code> of the tool window to be deactivated.
840 * @param shouldHide if <code>true</code> then also hides specified tool window.
842 private void deactivateToolWindowImpl(final String id, final boolean shouldHide, final List<FinalizableCommand> commandsList) {
843 if (LOG.isDebugEnabled()) {
844 LOG.debug("enter: deactivateToolWindowImpl(" + id + "," + shouldHide + ")");
846 final WindowInfoImpl info = getInfo(id);
847 if (shouldHide && info.isVisible()) {
848 info.setVisible(false);
849 if (info.isFloating()) {
850 appendRemoveFloatingDecoratorCmd(info, commandsList);
852 else if (info.isWindowed()) {
853 appendRemoveWindowedDecoratorCmd(info, commandsList);
855 else { // docked and sliding windows
856 appendRemoveDecoratorCmd(id, false, commandsList);
859 info.setActive(false);
860 appendApplyWindowInfoCmd(info, commandsList);
865 public String[] getToolWindowIds() {
866 final WindowInfoImpl[] infos = myLayout.getInfos();
867 final String[] ids = ArrayUtil.newStringArray(infos.length);
868 for (int i = 0; i < infos.length; i++) {
869 ids[i] = infos[i].getId();
875 public String getActiveToolWindowId() {
876 ApplicationManager.getApplication().assertIsDispatchThread();
877 return myLayout.getActiveId();
881 public String getLastActiveToolWindowId() {
882 return getLastActiveToolWindowId(null);
887 public String getLastActiveToolWindowId(@Nullable Condition<JComponent> condition) {
888 ApplicationManager.getApplication().assertIsDispatchThread();
889 String lastActiveToolWindowId = null;
890 for (int i = 0; i < myActiveStack.getPersistentSize(); i++) {
891 final String id = myActiveStack.peekPersistent(i);
892 final ToolWindow toolWindow = getToolWindow(id);
893 LOG.assertTrue(toolWindow != null);
894 if (toolWindow.isAvailable()) {
895 if (condition == null || condition.value(toolWindow.getComponent())) {
896 lastActiveToolWindowId = id;
901 return lastActiveToolWindowId;
905 * @return floating decorator for the tool window with specified <code>ID</code>.
907 private FloatingDecorator getFloatingDecorator(final String id) {
908 return myId2FloatingDecorator.get(id);
911 * @return windowed decorator for the tool window with specified <code>ID</code>.
913 private WindowedDecorator getWindowedDecorator(String id) {
914 return myId2WindowedDecorator.get(id);
917 * @return internal decorator for the tool window with specified <code>ID</code>.
919 private InternalDecorator getInternalDecorator(final String id) {
920 return myId2InternalDecorator.get(id);
924 * @return tool button for the window with specified <code>ID</code>.
926 private StripeButton getStripeButton(final String id) {
927 return myId2StripeButton.get(id);
931 * @return info for the tool window with specified <code>ID</code>.
933 private WindowInfoImpl getInfo(final String id) {
934 return myLayout.getInfo(id, true);
938 public List<String> getIdsOn(@NotNull final ToolWindowAnchor anchor) {
939 return myLayout.getVisibleIdsOn(anchor, this);
943 public ToolWindow getToolWindow(final String id) {
944 if (!myLayout.isToolWindowRegistered(id)) {
947 InternalDecorator decorator = getInternalDecorator(id);
948 return decorator != null ? decorator.getToolWindow() : null;
951 void showToolWindow(final String id) {
952 if (LOG.isDebugEnabled()) {
953 LOG.debug("enter: showToolWindow(" + id + ")");
955 ApplicationManager.getApplication().assertIsDispatchThread();
956 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
957 showToolWindowImpl(id, false, commandList);
958 execute(commandList);
962 public void hideToolWindow(@NotNull final String id, final boolean hideSide) {
963 hideToolWindow(id, hideSide, true);
966 public void hideToolWindow(final String id, final boolean hideSide, final boolean moveFocus) {
967 ApplicationManager.getApplication().assertIsDispatchThread();
969 final WindowInfoImpl info = getInfo(id);
970 if (!info.isVisible()) return;
971 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
972 final boolean wasActive = info.isActive();
974 // hide and deactivate
976 deactivateToolWindowImpl(id, true, commandList);
978 if (hideSide && !info.isFloating() && !info.isWindowed()) {
979 final List<String> ids = myLayout.getVisibleIdsOn(info.getAnchor(), this);
980 for (String each : ids) {
981 myActiveStack.remove(each, true);
985 while (!mySideStack.isEmpty(info.getAnchor())) {
986 mySideStack.pop(info.getAnchor());
989 final String[] all = getToolWindowIds();
990 for (String eachId : all) {
991 final WindowInfoImpl eachInfo = getInfo(eachId);
992 if (eachInfo.isVisible() && eachInfo.getAnchor() == info.getAnchor()) {
993 deactivateToolWindowImpl(eachId, true, commandList);
997 activateEditorComponentImpl(commandList, true);
999 else if (isStackEnabled()) {
1001 // first of all we have to find tool window that was located at the same side and
1004 WindowInfoImpl info2 = null;
1005 while (!mySideStack.isEmpty(info.getAnchor())) {
1006 final WindowInfoImpl storedInfo = mySideStack.pop(info.getAnchor());
1007 if (storedInfo.isSplit() != info.isSplit()) {
1011 final WindowInfoImpl currentInfo = getInfo(storedInfo.getId());
1012 LOG.assertTrue(currentInfo != null);
1013 // SideStack contains copies of real WindowInfos. It means that
1014 // these stored infos can be invalid. The following loop removes invalid WindowInfos.
1015 if (storedInfo.getAnchor() == currentInfo.getAnchor() &&
1016 storedInfo.getType() == currentInfo.getType() &&
1017 storedInfo.isAutoHide() == currentInfo.isAutoHide()) {
1022 if (info2 != null) {
1023 showToolWindowImpl(info2.getId(), false, commandList);
1026 // If we hide currently active tool window then we should activate the previous
1027 // one which is located in the tool window stack.
1028 // Activate another tool window if no active tool window exists and
1029 // window stack is enabled.
1031 myActiveStack.remove(id, false); // hidden window should be at the top of stack
1033 if (wasActive && moveFocus) {
1034 if (myActiveStack.isEmpty()) {
1035 if (hasOpenEditorFiles()) {
1036 activateEditorComponentImpl(commandList, false);
1039 focusToolWinowByDefault(id);
1043 final String toBeActivatedId = myActiveStack.pop();
1044 if (toBeActivatedId != null && (getInfo(toBeActivatedId).isVisible() || isStackEnabled())) {
1045 activateToolWindowImpl(toBeActivatedId, commandList, false, true);
1048 focusToolWinowByDefault(id);
1053 //todo[kb] it's just a temporary solution due a number of focus issues in JDK 7
1054 if (SystemInfo.isJavaVersionAtLeast("1.7") && moveFocus) {
1055 if (hasOpenEditorFiles()) {
1056 activateEditorComponentImpl(commandList, false);
1058 focusToolWinowByDefault(id);
1062 execute(commandList);
1065 private static boolean isStackEnabled() {
1066 return Registry.is("ide.enable.toolwindow.stack");
1070 * @param dirtyMode if <code>true</code> then all UI operations are performed in dirty mode.
1072 private void showToolWindowImpl(final String id, final boolean dirtyMode, final List<FinalizableCommand> commandsList) {
1073 final WindowInfoImpl toBeShownInfo = getInfo(id);
1074 if (toBeShownInfo.isVisible() || !getToolWindow(id).isAvailable()) {
1078 if (DumbService.getInstance(myProject).isDumb() && !myDumbAwareIds.contains(id)) {
1082 toBeShownInfo.setVisible(true);
1083 final InternalDecorator decorator = getInternalDecorator(id);
1085 if (toBeShownInfo.isFloating()) {
1086 commandsList.add(new AddFloatingDecoratorCmd(decorator, toBeShownInfo));
1088 else if (toBeShownInfo.isWindowed()) {
1089 commandsList.add(new AddWindowedDecoratorCmd(decorator, toBeShownInfo));
1091 else { // docked and sliding windows
1093 // If there is tool window on the same side then we have to hide it, i.e.
1094 // clear place for tool window to be shown.
1096 // We store WindowInfo of hidden tool window in the SideStack (if the tool window
1097 // is docked and not auto-hide one). Therefore it's possible to restore the
1098 // hidden tool window when showing tool window will be closed.
1100 final WindowInfoImpl[] infos = myLayout.getInfos();
1101 for (final WindowInfoImpl info : infos) {
1102 if (id.equals(info.getId())) {
1105 if (info.isVisible() &&
1106 info.getType() == toBeShownInfo.getType() &&
1107 info.getAnchor() == toBeShownInfo.getAnchor() &&
1108 info.isSplit() == toBeShownInfo.isSplit()) {
1109 // hide and deactivate tool window
1110 info.setVisible(false);
1111 appendRemoveDecoratorCmd(info.getId(), false, commandsList);
1112 if (info.isActive()) {
1113 info.setActive(false);
1115 appendApplyWindowInfoCmd(info, commandsList);
1116 // store WindowInfo into the SideStack
1117 if (info.isDocked() && !info.isAutoHide()) {
1118 mySideStack.push(info);
1122 appendAddDecoratorCmd(decorator, toBeShownInfo, dirtyMode, commandsList);
1124 // Remove tool window from the SideStack.
1126 mySideStack.remove(id);
1129 if (!toBeShownInfo.isShowStripeButton()) {
1130 toBeShownInfo.setShowStripeButton(true);
1133 appendApplyWindowInfoCmd(toBeShownInfo, commandsList);
1138 public ToolWindow registerToolWindow(@NotNull final String id,
1139 @NotNull final JComponent component,
1140 @NotNull final ToolWindowAnchor anchor) {
1141 return registerToolWindow(id, component, anchor, false);
1146 public ToolWindow registerToolWindow(@NotNull final String id,
1147 @NotNull JComponent component,
1148 @NotNull ToolWindowAnchor anchor,
1149 @NotNull Disposable parentDisposable) {
1150 return registerToolWindow(id, component, anchor, parentDisposable, false, false);
1155 public ToolWindow registerToolWindow(@NotNull String id,
1156 @NotNull JComponent component,
1157 @NotNull ToolWindowAnchor anchor,
1158 @NotNull Disposable parentDisposable,
1159 boolean canWorkInDumbMode) {
1160 return registerToolWindow(id, component, anchor, parentDisposable, canWorkInDumbMode, false);
1165 public ToolWindow registerToolWindow(@NotNull final String id,
1166 @NotNull JComponent component,
1167 @NotNull ToolWindowAnchor anchor,
1168 @NotNull Disposable parentDisposable,
1169 boolean canWorkInDumbMode, boolean canCloseContents) {
1170 return registerDisposable(id, parentDisposable, registerToolWindow(id, component, anchor, false, canCloseContents, canWorkInDumbMode));
1174 private ToolWindow registerToolWindow(@NotNull final String id,
1175 @NotNull final JComponent component,
1176 @NotNull final ToolWindowAnchor anchor,
1177 boolean canWorkInDumbMode) {
1178 return registerToolWindow(id, component, anchor, false, false, canWorkInDumbMode);
1183 public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor) {
1184 return registerToolWindow(id, null, anchor, false, canCloseContent, false);
1189 public ToolWindow registerToolWindow(@NotNull final String id,
1190 final boolean canCloseContent,
1191 @NotNull final ToolWindowAnchor anchor,
1192 final boolean secondary) {
1193 return registerToolWindow(id, null, anchor, secondary, canCloseContent, false);
1199 public ToolWindow registerToolWindow(@NotNull final String id,
1200 final boolean canCloseContent,
1201 @NotNull final ToolWindowAnchor anchor,
1202 @NotNull final Disposable parentDisposable,
1203 final boolean canWorkInDumbMode) {
1204 return registerToolWindow(id, canCloseContent, anchor, parentDisposable, canWorkInDumbMode, false);
1209 public ToolWindow registerToolWindow(@NotNull String id,
1210 boolean canCloseContent,
1211 @NotNull ToolWindowAnchor anchor,
1212 Disposable parentDisposable,
1213 boolean canWorkInDumbMode,
1214 boolean secondary) {
1215 ToolWindow window = registerToolWindow(id, null, anchor, secondary, canCloseContent, canWorkInDumbMode);
1216 return registerDisposable(id, parentDisposable, window);
1220 private ToolWindow registerToolWindow(@NotNull final String id,
1221 @Nullable final JComponent component,
1222 @NotNull final ToolWindowAnchor anchor,
1224 boolean canCloseContent,
1225 final boolean canWorkInDumbMode) {
1226 if (LOG.isDebugEnabled()) {
1227 LOG.debug("enter: installToolWindow(" + id + "," + component + "," + anchor + "\")");
1229 ApplicationManager.getApplication().assertIsDispatchThread();
1230 if (myLayout.isToolWindowRegistered(id)) {
1231 throw new IllegalArgumentException("window with id=\"" + id + "\" is already registered");
1234 final WindowInfoImpl info = myLayout.register(id, anchor, sideTool);
1235 final boolean wasActive = info.isActive();
1236 final boolean wasVisible = info.isVisible();
1237 info.setActive(false);
1238 info.setVisible(false);
1242 ToolWindowImpl toolWindow = new ToolWindowImpl(this, id, canCloseContent, component);
1243 InternalDecorator decorator = new InternalDecorator(myProject, info.copy(), toolWindow);
1244 ActivateToolWindowAction.ensureToolWindowActionRegistered(toolWindow);
1245 myId2InternalDecorator.put(id, decorator);
1246 decorator.addInternalDecoratorListener(myInternalDecoratorListener);
1247 toolWindow.addPropertyChangeListener(myToolWindowPropertyChangeListener);
1248 myId2FocusWatcher.put(id, new ToolWindowFocusWatcher(toolWindow));
1250 // Create and show tool button
1252 final StripeButton button = new StripeButton(decorator, myToolWindowsPane);
1253 myId2StripeButton.put(id, button);
1254 List<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
1255 appendAddButtonCmd(button, info, commandsList);
1257 if (canWorkInDumbMode) {
1258 myDumbAwareIds.add(id);
1260 else if (DumbService.getInstance(getProject()).isDumb()) {
1261 button.setEnabled(false);
1264 // If preloaded info is visible or active then we have to show/activate the installed
1265 // tool window. This step has sense only for windows which are not in the autohide
1266 // mode. But if tool window was active but its mode doen't allow to activate it again
1267 // (for example, tool window is in autohide mode) then we just activate editor component.
1269 if (!info.isAutoHide() && (info.isDocked() || info.isFloating())) {
1271 activateToolWindowImpl(info.getId(), commandsList, true, true);
1273 else if (wasVisible) {
1274 showToolWindowImpl(info.getId(), false, commandsList);
1277 else if (wasActive) { // tool window was active but it cannot be activate again
1278 activateEditorComponentImpl(commandsList, true);
1281 execute(commandsList);
1282 fireToolWindowRegistered(id);
1287 private ToolWindow registerDisposable(@NotNull final String id, @NotNull final Disposable parentDisposable, @NotNull ToolWindow window) {
1288 Disposer.register(parentDisposable, new Disposable() {
1290 public void dispose() {
1291 unregisterToolWindow(id);
1298 public void unregisterToolWindow(@NotNull final String id) {
1299 if (LOG.isDebugEnabled()) {
1300 LOG.debug("enter: unregisterToolWindow(" + id + ")");
1302 ApplicationManager.getApplication().assertIsDispatchThread();
1303 if (!myLayout.isToolWindowRegistered(id)) {
1307 final WindowInfoImpl info = getInfo(id);
1308 final ToolWindowEx toolWindow = (ToolWindowEx)getToolWindow(id);
1309 // Save recent appearance of tool window
1310 myLayout.unregister(id);
1311 // Remove decorator and tool button from the screen
1312 final ArrayList<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
1313 if (info.isVisible()) {
1314 info.setVisible(false);
1315 if (info.isFloating()) {
1316 appendRemoveFloatingDecoratorCmd(info, commandsList);
1318 else if (info.isWindowed()) {
1319 appendRemoveWindowedDecoratorCmd(info, commandsList);
1321 else { // floating and sliding windows
1322 appendRemoveDecoratorCmd(id, false, commandsList);
1325 appendRemoveButtonCmd(id, commandsList);
1326 appendApplyWindowInfoCmd(info, commandsList);
1327 execute(commandsList);
1328 // Remove all references on tool window and save its last properties
1329 toolWindow.removePropertyChangeListener(myToolWindowPropertyChangeListener);
1330 myActiveStack.remove(id, true);
1331 mySideStack.remove(id);
1332 // Destroy stripe button
1333 final StripeButton button = getStripeButton(id);
1334 Disposer.dispose(button);
1335 myId2StripeButton.remove(id);
1337 ToolWindowFocusWatcher watcher = (ToolWindowFocusWatcher)myId2FocusWatcher.remove(id);
1338 watcher.deinstall();
1340 // Destroy decorator
1341 final InternalDecorator decorator = getInternalDecorator(id);
1342 decorator.dispose();
1343 decorator.removeInternalDecoratorListener(myInternalDecoratorListener);
1344 myId2InternalDecorator.remove(id);
1348 public DesktopLayout getLayout() {
1349 ApplicationManager.getApplication().assertIsDispatchThread();
1354 public void setLayoutToRestoreLater(DesktopLayout layout) {
1355 myLayoutToRestoreLater = layout;
1359 public DesktopLayout getLayoutToRestoreLater() {
1360 return myLayoutToRestoreLater;
1364 public void setLayout(@NotNull final DesktopLayout layout) {
1365 ApplicationManager.getApplication().assertIsDispatchThread();
1366 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1367 // hide tool window that are invisible in new layout
1368 final WindowInfoImpl[] currentInfos = myLayout.getInfos();
1369 for (final WindowInfoImpl currentInfo : currentInfos) {
1370 final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1374 if (currentInfo.isVisible() && !info.isVisible()) {
1375 deactivateToolWindowImpl(currentInfo.getId(), true, commandList);
1378 // change anchor of tool windows
1379 for (final WindowInfoImpl currentInfo : currentInfos) {
1380 final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1384 if (currentInfo.getAnchor() != info.getAnchor() || currentInfo.getOrder() != info.getOrder()) {
1385 setToolWindowAnchorImpl(currentInfo.getId(), info.getAnchor(), info.getOrder(), commandList);
1388 // change types of tool windows
1389 for (final WindowInfoImpl currentInfo : currentInfos) {
1390 final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1394 if (currentInfo.getType() != info.getType()) {
1395 setToolWindowTypeImpl(currentInfo.getId(), info.getType(), commandList);
1398 // change auto-hide state
1399 for (final WindowInfoImpl currentInfo : currentInfos) {
1400 final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1404 if (currentInfo.isAutoHide() != info.isAutoHide()) {
1405 setToolWindowAutoHideImpl(currentInfo.getId(), info.isAutoHide(), commandList);
1408 // restore visibility
1409 for (final WindowInfoImpl currentInfo : currentInfos) {
1410 final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1414 if (info.isVisible()) {
1415 showToolWindowImpl(currentInfo.getId(), false, commandList);
1418 // if there is no any active tool window and editor is also inactive
1419 // then activate editor
1420 if (!myEditorWasActive && getActiveToolWindowId() == null) {
1421 activateEditorComponentImpl(commandList, true);
1423 execute(commandList);
1427 public void invokeLater(@NotNull final Runnable runnable) {
1428 List<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1429 commandList.add(new InvokeLaterCmd(runnable, myWindowManager.getCommandProcessor()));
1430 execute(commandList);
1435 public IdeFocusManager getFocusManager() {
1436 return IdeFocusManager.getInstance(myProject);
1440 public boolean canShowNotification(@NotNull final String toolWindowId) {
1441 if (!Arrays.asList(getToolWindowIds()).contains(toolWindowId)) {
1444 final Stripe stripe = myToolWindowsPane.getStripeFor(toolWindowId);
1445 return stripe != null && stripe.getButtonFor(toolWindowId) != null;
1449 public void notifyByBalloon(@NotNull final String toolWindowId, @NotNull final MessageType type, @NotNull final String htmlBody) {
1450 notifyByBalloon(toolWindowId, type, htmlBody, null, null);
1454 public void notifyByBalloon(@NotNull final String toolWindowId,
1455 @NotNull final MessageType type,
1456 @NotNull final String text,
1457 @Nullable final Icon icon,
1458 @Nullable final HyperlinkListener listener) {
1459 checkId(toolWindowId);
1462 Balloon existing = myWindow2Balloon.get(toolWindowId);
1463 if (existing != null) {
1467 final Stripe stripe = myToolWindowsPane.getStripeFor(toolWindowId);
1468 if (stripe == null) {
1471 final ToolWindowImpl window = getInternalDecorator(toolWindowId).getToolWindow();
1472 if (!window.isAvailable()) {
1473 window.setPlaceholderMode(true);
1474 stripe.updatePresentation();
1475 stripe.revalidate();
1479 final ToolWindowAnchor anchor = getInfo(toolWindowId).getAnchor();
1480 final Ref<Balloon.Position> position = Ref.create(Balloon.Position.below);
1481 if (ToolWindowAnchor.TOP == anchor) {
1482 position.set(Balloon.Position.below);
1484 else if (ToolWindowAnchor.BOTTOM == anchor) {
1485 position.set(Balloon.Position.above);
1487 else if (ToolWindowAnchor.LEFT == anchor) {
1488 position.set(Balloon.Position.atRight);
1490 else if (ToolWindowAnchor.RIGHT == anchor) {
1491 position.set(Balloon.Position.atLeft);
1494 final BalloonHyperlinkListener listenerWrapper = new BalloonHyperlinkListener(listener);
1495 final Balloon balloon =
1496 JBPopupFactory.getInstance()
1497 .createHtmlTextBalloonBuilder(text.replace("\n", "<br>"), icon, type.getPopupBackground(), listenerWrapper)
1498 .setHideOnClickOutside(false).setHideOnFrameResize(false).createBalloon();
1499 FrameStateManager.getInstance().getApplicationActive().doWhenDone(new Runnable() {
1502 final Alarm alarm = new Alarm();
1503 alarm.addRequest(new Runnable() {
1506 ((BalloonImpl)balloon).setHideOnClickOutside(true);
1507 Disposer.dispose(alarm);
1512 listenerWrapper.myBalloon = balloon;
1513 myWindow2Balloon.put(toolWindowId, balloon);
1514 Disposer.register(balloon, new Disposable() {
1516 public void dispose() {
1517 window.setPlaceholderMode(false);
1518 stripe.updatePresentation();
1519 stripe.revalidate();
1521 myWindow2Balloon.remove(toolWindowId);
1524 Disposer.register(getProject(), balloon);
1526 execute(new ArrayList<FinalizableCommand>(Arrays.<FinalizableCommand>asList(new FinalizableCommand(null) {
1529 final StripeButton button = stripe.getButtonFor(toolWindowId);
1530 LOG.assertTrue(button != null, "Button was not found, popup won't be shown. Toolwindow id: " +
1534 ", message type: " +
1536 if (button == null) return;
1538 final Runnable show = new Runnable() {
1541 if (button.isShowing()) {
1542 PositionTracker<Balloon> tracker = new PositionTracker<Balloon>(button) {
1545 public RelativePoint recalculateLocation(Balloon object) {
1546 Stripe twStripe = myToolWindowsPane.getStripeFor(toolWindowId);
1547 StripeButton twButton = twStripe != null ? twStripe.getButtonFor(toolWindowId) : null;
1549 if (twButton == null) return null;
1551 if (getToolWindow(toolWindowId).getAnchor() != anchor) {
1556 final Point point = new Point(twButton.getBounds().width / 2, twButton.getHeight() / 2 - 2);
1557 return new RelativePoint(twButton, point);
1560 if (!balloon.isDisposed()) {
1561 balloon.show(tracker, position.get());
1565 final Rectangle bounds = myToolWindowsPane.getBounds();
1566 final Point target = UIUtil.getCenterPoint(bounds, new Dimension(1, 1));
1567 if (ToolWindowAnchor.TOP == anchor) {
1570 else if (ToolWindowAnchor.BOTTOM == anchor) {
1571 target.y = bounds.height - 3;
1573 else if (ToolWindowAnchor.LEFT == anchor) {
1576 else if (ToolWindowAnchor.RIGHT == anchor) {
1577 target.x = bounds.width;
1579 if (!balloon.isDisposed()) {
1580 balloon.show(new RelativePoint(myToolWindowsPane, target), position.get());
1586 if (!button.isValid()) {
1587 SwingUtilities.invokeLater(new Runnable() {
1602 public Balloon getToolWindowBalloon(String id) {
1603 return myWindow2Balloon.get(id);
1607 public boolean isEditorComponentActive() {
1608 ApplicationManager.getApplication().assertIsDispatchThread();
1610 Component owner = getFocusManager().getFocusOwner();
1611 EditorsSplitters splitters = UIUtil.getParentOfType(EditorsSplitters.class, owner);
1612 return splitters != null;
1615 ToolWindowAnchor getToolWindowAnchor(final String id) {
1617 return getInfo(id).getAnchor();
1620 void setToolWindowAnchor(final String id, final ToolWindowAnchor anchor) {
1621 ApplicationManager.getApplication().assertIsDispatchThread();
1622 setToolWindowAnchor(id, anchor, -1);
1625 void setToolWindowAnchor(final String id, final ToolWindowAnchor anchor, final int order) {
1626 ApplicationManager.getApplication().assertIsDispatchThread();
1627 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1628 setToolWindowAnchorImpl(id, anchor, order, commandList);
1629 execute(commandList);
1632 private void setToolWindowAnchorImpl(final String id,
1633 final ToolWindowAnchor anchor,
1635 final ArrayList<FinalizableCommand> commandsList) {
1637 final WindowInfoImpl info = getInfo(id);
1638 if (anchor == info.getAnchor() && order == info.getOrder()) {
1641 // if tool window isn't visible or only order number is changed then just remove/add stripe button
1642 if (!info.isVisible() || anchor == info.getAnchor() || info.isFloating()) {
1643 appendRemoveButtonCmd(id, commandsList);
1644 myLayout.setAnchor(id, anchor, order);
1645 // update infos for all window. Actually we have to update only infos affected by
1647 final WindowInfoImpl[] infos = myLayout.getInfos();
1648 for (WindowInfoImpl info1 : infos) {
1649 appendApplyWindowInfoCmd(info1, commandsList);
1651 appendAddButtonCmd(getStripeButton(id), info, commandsList);
1653 else { // for docked and sliding windows we have to move buttons and window's decorators
1654 info.setVisible(false);
1655 appendRemoveDecoratorCmd(id, false, commandsList);
1656 appendRemoveButtonCmd(id, commandsList);
1657 myLayout.setAnchor(id, anchor, order);
1658 // update infos for all window. Actually we have to update only infos affected by
1660 final WindowInfoImpl[] infos = myLayout.getInfos();
1661 for (WindowInfoImpl info1 : infos) {
1662 appendApplyWindowInfoCmd(info1, commandsList);
1664 appendAddButtonCmd(getStripeButton(id), info, commandsList);
1665 showToolWindowImpl(id, false, commandsList);
1666 if (info.isActive()) {
1667 appendRequestFocusInToolWindowCmd(id, commandsList, true);
1672 boolean isSplitMode(String id) {
1673 ApplicationManager.getApplication().assertIsDispatchThread();
1675 return getInfo(id).isSplit();
1679 ToolWindowContentUiType getContentUiType(String id) {
1680 ApplicationManager.getApplication().assertIsDispatchThread();
1682 return getInfo(id).getContentUiType();
1685 void setSideTool(String id, boolean isSide) {
1686 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1687 setSplitModeImpl(id, isSide, commandList);
1688 execute(commandList);
1691 public void setContentUiType(String id, @NotNull ToolWindowContentUiType type) {
1692 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1694 WindowInfoImpl info = getInfo(id);
1695 info.setContentUiType(type);
1696 appendApplyWindowInfoCmd(info, commandList);
1697 execute(commandList);
1700 void setSideToolAndAnchor(String id, ToolWindowAnchor anchor, int order, boolean isSide) {
1701 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1702 setToolWindowAnchor(id, anchor, order);
1703 setSplitModeImpl(id, isSide, commandList);
1704 execute(commandList);
1707 private void setSplitModeImpl(final String id, final boolean isSplit, final ArrayList<FinalizableCommand> commandList) {
1709 final WindowInfoImpl info = getInfo(id);
1710 if (isSplit == info.isSplit()) {
1714 myLayout.setSplitMode(id, isSplit);
1716 boolean wasActive = info.isActive();
1718 deactivateToolWindowImpl(id, true, commandList);
1720 final WindowInfoImpl[] infos = myLayout.getInfos();
1721 for (WindowInfoImpl info1 : infos) {
1722 appendApplyWindowInfoCmd(info1, commandList);
1725 activateToolWindowImpl(id, commandList, true, true);
1727 commandList.add(myToolWindowsPane.createUpdateButtonPositionCmd(id, myWindowManager.getCommandProcessor()));
1730 ToolWindowType getToolWindowInternalType(final String id) {
1731 ApplicationManager.getApplication().assertIsDispatchThread();
1733 return getInfo(id).getInternalType();
1736 ToolWindowType getToolWindowType(final String id) {
1738 return getInfo(id).getType();
1741 private void fireToolWindowRegistered(final String id) {
1742 myDispatcher.getMulticaster().toolWindowRegistered(id);
1745 private void fireStateChanged() {
1746 myDispatcher.getMulticaster().stateChanged();
1749 boolean isToolWindowActive(final String id) {
1750 ApplicationManager.getApplication().assertIsDispatchThread();
1752 return getInfo(id).isActive();
1755 boolean isToolWindowAutoHide(final String id) {
1756 ApplicationManager.getApplication().assertIsDispatchThread();
1758 return getInfo(id).isAutoHide();
1761 boolean isToolWindowVisible(final String id) {
1763 return getInfo(id).isVisible();
1766 void setToolWindowAutoHide(final String id, final boolean autoHide) {
1767 ApplicationManager.getApplication().assertIsDispatchThread();
1768 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1769 setToolWindowAutoHideImpl(id, autoHide, commandList);
1770 execute(commandList);
1773 private void setToolWindowAutoHideImpl(final String id, final boolean autoHide, final ArrayList<FinalizableCommand> commandsList) {
1775 final WindowInfoImpl info = getInfo(id);
1776 if (info.isAutoHide() == autoHide) {
1779 info.setAutoHide(autoHide);
1780 appendApplyWindowInfoCmd(info, commandsList);
1781 if (info.isVisible()) {
1782 deactivateWindows(id, commandsList);
1783 showAndActivate(id, false, commandsList, true, true);
1787 void setToolWindowType(final String id, final ToolWindowType type) {
1788 ApplicationManager.getApplication().assertIsDispatchThread();
1789 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1790 setToolWindowTypeImpl(id, type, commandList);
1791 execute(commandList);
1794 private void setToolWindowTypeImpl(final String id, final ToolWindowType type, final ArrayList<FinalizableCommand> commandsList) {
1796 final WindowInfoImpl info = getInfo(id);
1797 if (info.getType() == type) {
1800 if (info.isVisible()) {
1801 final boolean dirtyMode = info.isDocked() || info.isSliding();
1802 info.setVisible(false);
1803 if (info.isFloating()) {
1804 appendRemoveFloatingDecoratorCmd(info, commandsList);
1806 else if (info.isWindowed()) {
1807 appendRemoveWindowedDecoratorCmd(info, commandsList);
1809 else { // docked and sliding windows
1810 appendRemoveDecoratorCmd(id, dirtyMode, commandsList);
1813 appendApplyWindowInfoCmd(info, commandsList);
1814 deactivateWindows(id, commandsList);
1815 showAndActivate(id, dirtyMode, commandsList, true, true);
1816 appendUpdateToolWindowsPaneCmd(commandsList);
1820 appendApplyWindowInfoCmd(info, commandsList);
1824 private void appendApplyWindowInfoCmd(final WindowInfoImpl info, final List<FinalizableCommand> commandsList) {
1825 final StripeButton button = getStripeButton(info.getId());
1826 final InternalDecorator decorator = getInternalDecorator(info.getId());
1827 commandsList.add(new ApplyWindowInfoCmd(info, button, decorator, myWindowManager.getCommandProcessor()));
1831 * @see ToolWindowsPane#createAddDecoratorCmd
1833 private void appendAddDecoratorCmd(final InternalDecorator decorator,
1834 final WindowInfoImpl info,
1835 final boolean dirtyMode,
1836 final List<FinalizableCommand> commandsList) {
1837 final CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1838 final FinalizableCommand command = myToolWindowsPane.createAddDecoratorCmd(decorator, info, dirtyMode, commandProcessor);
1839 commandsList.add(command);
1843 * @see ToolWindowsPane#createRemoveDecoratorCmd
1845 private void appendRemoveDecoratorCmd(final String id, final boolean dirtyMode, final List<FinalizableCommand> commandsList) {
1846 final FinalizableCommand command = myToolWindowsPane.createRemoveDecoratorCmd(id, dirtyMode, myWindowManager.getCommandProcessor());
1847 commandsList.add(command);
1850 private void appendRemoveFloatingDecoratorCmd(final WindowInfoImpl info, final List<FinalizableCommand> commandsList) {
1851 final RemoveFloatingDecoratorCmd command = new RemoveFloatingDecoratorCmd(info);
1852 commandsList.add(command);
1855 private void appendRemoveWindowedDecoratorCmd(final WindowInfoImpl info, final List<FinalizableCommand> commandsList) {
1856 final RemoveWindowedDecoratorCmd command = new RemoveWindowedDecoratorCmd(info);
1857 commandsList.add(command);
1861 * @see ToolWindowsPane#createAddButtonCmd
1863 private void appendAddButtonCmd(final StripeButton button, final WindowInfoImpl info, final List<FinalizableCommand> commandsList) {
1864 final Comparator<StripeButton> comparator = myLayout.comparator(info.getAnchor());
1865 final CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1866 final FinalizableCommand command = myToolWindowsPane.createAddButtonCmd(button, info, comparator, commandProcessor);
1867 commandsList.add(command);
1871 * @see ToolWindowsPane#createAddButtonCmd
1873 private void appendRemoveButtonCmd(final String id, final List<FinalizableCommand> commandsList) {
1874 final FinalizableCommand command = myToolWindowsPane.createRemoveButtonCmd(id, myWindowManager.getCommandProcessor());
1875 commandsList.add(command);
1878 private ActionCallback appendRequestFocusInEditorComponentCmd(List<FinalizableCommand> commandList, boolean forced) {
1879 if (myProject.isDisposed()) return ActionCallback.DONE;
1880 EditorsSplitters splitters = getSplittersToFocus();
1881 CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1882 RequestFocusInEditorComponentCmd command = new RequestFocusInEditorComponentCmd(splitters, getFocusManager(), commandProcessor, forced);
1883 commandList.add(command);
1884 return command.getDoneCallback();
1887 private void appendRequestFocusInToolWindowCmd(final String id, List<FinalizableCommand> commandList, boolean forced) {
1888 final ToolWindowImpl toolWindow = (ToolWindowImpl)getToolWindow(id);
1889 final FocusWatcher focusWatcher = myId2FocusWatcher.get(id);
1891 .add(new RequestFocusInToolWindowCmd(getFocusManager(), toolWindow, focusWatcher, myWindowManager.getCommandProcessor(), forced));
1895 * @see ToolWindowsPane#createSetEditorComponentCmd
1897 public void appendSetEditorComponentCmd(@Nullable final JComponent component, final List<FinalizableCommand> commandsList) {
1898 final CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1899 final FinalizableCommand command = myToolWindowsPane.createSetEditorComponentCmd(component, commandProcessor);
1900 commandsList.add(command);
1903 private void appendUpdateToolWindowsPaneCmd(final List<FinalizableCommand> commandsList) {
1904 final JRootPane rootPane = myFrame.getRootPane();
1905 if (rootPane != null) {
1906 final FinalizableCommand command = new UpdateRootPaneCmd(rootPane, myWindowManager.getCommandProcessor());
1907 commandsList.add(command);
1911 private EditorsSplitters getSplittersToFocus() {
1912 Window activeWindow = myWindowManager.getMostRecentFocusedWindow();
1914 if (activeWindow instanceof FloatingDecorator) {
1915 IdeFocusManager ideFocusManager = IdeFocusManager.findInstanceByComponent(activeWindow);
1916 IdeFrame lastFocusedFrame = ideFocusManager.getLastFocusedFrame();
1917 JComponent frameComponent = lastFocusedFrame != null ? lastFocusedFrame.getComponent() : null;
1918 Window lastFocusedWindow = frameComponent != null ? SwingUtilities.getWindowAncestor(frameComponent) : null;
1919 activeWindow = ObjectUtils.notNull(lastFocusedWindow, activeWindow);
1922 FileEditorManagerEx fem = FileEditorManagerEx.getInstanceEx(myProject);
1923 EditorsSplitters splitters = activeWindow != null ? fem.getSplittersFor(activeWindow) : null;
1924 return splitters != null ? splitters : fem.getSplitters();
1928 * @return <code>true</code> if tool window with the specified <code>id</code>
1929 * is floating and has modal showing child dialog. Such windows should not be closed
1930 * when auto-hide windows are gone.
1932 private boolean hasModalChild(final WindowInfoImpl info) {
1933 if (!info.isVisible() || !info.isFloating()) {
1936 final FloatingDecorator decorator = getFloatingDecorator(info.getId());
1937 LOG.assertTrue(decorator != null);
1938 return isModalOrHasModalChild(decorator);
1941 private static boolean isModalOrHasModalChild(final Window window) {
1942 if (window instanceof Dialog) {
1943 final Dialog dialog = (Dialog)window;
1944 if (dialog.isModal() && dialog.isShowing()) {
1947 final Window[] ownedWindows = dialog.getOwnedWindows();
1948 for (int i = ownedWindows.length - 1; i >= 0; i--) {
1949 if (isModalOrHasModalChild(ownedWindows[i])) {
1958 public void clearSideStack() {
1959 mySideStack.clear();
1963 public void readExternal(final Element element) {
1964 for (final Object o : element.getChildren()) {
1965 final Element e = (Element)o;
1966 if (EDITOR_ELEMENT.equals(e.getName())) {
1967 myEditorWasActive = Boolean.valueOf(e.getAttributeValue(ACTIVE_ATTR_VALUE)).booleanValue();
1969 else if (DesktopLayout.TAG.equals(e.getName())) { // read layout of tool windows
1970 myLayout.readExternal(e);
1972 else if (LAYOUT_TO_RESTORE.equals(e.getName())) {
1973 myLayoutToRestoreLater = new DesktopLayout();
1974 myLayoutToRestoreLater.readExternal(e);
1980 public void writeExternal(final Element element) {
1981 if (myFrame == null) {
1982 // do nothing if the project was not opened
1985 final String[] ids = getToolWindowIds();
1987 // Update size of all open floating windows. See SCR #18439
1988 for (final String id : ids) {
1989 final WindowInfoImpl info = getInfo(id);
1990 if (info.isVisible()) {
1991 final InternalDecorator decorator = getInternalDecorator(id);
1992 LOG.assertTrue(decorator != null);
1993 decorator.fireResized();
1997 // Save frame's bounds
1998 final Rectangle frameBounds = myFrame.getBounds();
1999 final Element frameElement = new Element(FRAME_ELEMENT);
2000 element.addContent(frameElement);
2001 frameElement.setAttribute(X_ATTR, Integer.toString(frameBounds.x));
2002 frameElement.setAttribute(Y_ATTR, Integer.toString(frameBounds.y));
2003 frameElement.setAttribute(WIDTH_ATTR, Integer.toString(frameBounds.width));
2004 frameElement.setAttribute(HEIGHT_ATTR, Integer.toString(frameBounds.height));
2005 frameElement.setAttribute(EXTENDED_STATE_ATTR, Integer.toString(myFrame.getExtendedState()));
2006 // Save whether editor is active or not
2007 final Element editorElement = new Element(EDITOR_ELEMENT);
2008 editorElement.setAttribute(ACTIVE_ATTR_VALUE, isEditorComponentActive() ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
2009 element.addContent(editorElement);
2010 // Save layout of tool windows
2011 final Element layoutElement = new Element(DesktopLayout.TAG);
2012 element.addContent(layoutElement);
2013 myLayout.writeExternal(layoutElement);
2014 if (myLayoutToRestoreLater != null) {
2015 Element layoutToRestoreElement = new Element(LAYOUT_TO_RESTORE);
2016 element.addContent(layoutToRestoreElement);
2017 myLayoutToRestoreLater.writeExternal(layoutToRestoreElement);
2021 public void setDefaultState(@NotNull final ToolWindowImpl toolWindow,
2022 @Nullable final ToolWindowAnchor anchor,
2023 @Nullable final ToolWindowType type,
2024 @Nullable final Rectangle floatingBounds) {
2026 final WindowInfoImpl info = getInfo(toolWindow.getId());
2027 if (info.wasRead()) return;
2029 if (floatingBounds != null) {
2030 info.setFloatingBounds(floatingBounds);
2033 if (anchor != null) {
2034 toolWindow.setAnchor(anchor, null);
2038 toolWindow.setType(type, null);
2042 public void setDefaultContentUiType(ToolWindowImpl toolWindow, @NotNull ToolWindowContentUiType type) {
2043 final WindowInfoImpl info = getInfo(toolWindow.getId());
2044 if (info.wasRead()) return;
2045 toolWindow.setContentUiType(type, null);
2049 public void stretchWidth(ToolWindowImpl toolWindow, int value) {
2050 myToolWindowsPane.stretchWidth(toolWindow, value);
2054 public boolean isMaximized(@NotNull ToolWindow wnd) {
2055 return myToolWindowsPane.isMaximized(wnd);
2059 public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) {
2060 myToolWindowsPane.setMaximized(wnd, maximized);
2063 public void stretchHeight(ToolWindowImpl toolWindow, int value) {
2064 myToolWindowsPane.stretchHeight(toolWindow, value);
2067 private static class BalloonHyperlinkListener implements HyperlinkListener {
2068 private Balloon myBalloon;
2069 private final HyperlinkListener myListener;
2071 public BalloonHyperlinkListener(HyperlinkListener listener) {
2072 myListener = listener;
2076 public void hyperlinkUpdate(HyperlinkEvent e) {
2077 if (myBalloon != null && e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
2080 if (myListener != null) {
2081 myListener.hyperlinkUpdate(e);
2088 * This command creates and shows <code>FloatingDecorator</code>.
2090 private final class AddFloatingDecoratorCmd extends FinalizableCommand {
2091 private final FloatingDecorator myFloatingDecorator;
2094 * Creates floating decorator for specified floating decorator.
2096 private AddFloatingDecoratorCmd(final InternalDecorator decorator, final WindowInfoImpl info) {
2097 super(myWindowManager.getCommandProcessor());
2098 myFloatingDecorator = new FloatingDecorator(myFrame, info.copy(), decorator);
2099 myId2FloatingDecorator.put(info.getId(), myFloatingDecorator);
2100 final Rectangle bounds = info.getFloatingBounds();
2101 if (bounds != null &&
2103 bounds.height > 0 &&
2104 myWindowManager.isInsideScreenBounds(bounds.x, bounds.y, bounds.width)) {
2105 myFloatingDecorator.setBounds(bounds);
2107 else { // place new frame at the center of main frame if there are no floating bounds
2108 Dimension size = decorator.getSize();
2109 if (size.width == 0 || size.height == 0) {
2110 size = decorator.getPreferredSize();
2112 myFloatingDecorator.setSize(size);
2113 myFloatingDecorator.setLocationRelativeTo(myFrame);
2120 myFloatingDecorator.show();
2129 * This command hides and destroys floating decorator for tool window
2130 * with specified <code>ID</code>.
2132 private final class RemoveFloatingDecoratorCmd extends FinalizableCommand {
2133 private final FloatingDecorator myFloatingDecorator;
2135 private RemoveFloatingDecoratorCmd(final WindowInfoImpl info) {
2136 super(myWindowManager.getCommandProcessor());
2137 myFloatingDecorator = getFloatingDecorator(info.getId());
2138 myId2FloatingDecorator.remove(info.getId());
2139 info.setFloatingBounds(myFloatingDecorator.getBounds());
2145 myFloatingDecorator.dispose();
2154 public Condition getExpireCondition() {
2155 return ApplicationManager.getApplication().getDisposed();
2160 * This command creates and shows <code>WindowedDecorator</code>.
2162 private final class AddWindowedDecoratorCmd extends FinalizableCommand {
2163 private final WindowedDecorator myWindowedDecorator;
2166 * Creates floating decorator for specified floating decorator.
2168 private AddWindowedDecoratorCmd(final InternalDecorator decorator, final WindowInfoImpl info) {
2169 super(myWindowManager.getCommandProcessor());
2170 myWindowedDecorator = new WindowedDecorator(myProject, info.copy(), decorator);
2171 Window window = myWindowedDecorator.getFrame();
2172 final Rectangle bounds = info.getFloatingBounds();
2173 if (bounds != null) {
2174 bounds.setBounds(bounds.x + DIVIDER_WIDTH, bounds.y + DIVIDER_WIDTH, bounds.width - 2 * DIVIDER_WIDTH, bounds.height - 2 * DIVIDER_WIDTH);
2176 if (bounds != null &&
2178 bounds.height > 0 &&
2179 myWindowManager.isInsideScreenBounds(bounds.x, bounds.y, bounds.width)) {
2180 window.setBounds(bounds);
2182 else { // place new frame at the center of main frame if there are no floating bounds
2183 Dimension size = decorator.getSize();
2184 if (size.width == 0 || size.height == 0) {
2185 size = decorator.getPreferredSize();
2187 window.setSize(size);
2188 window.setLocationRelativeTo(myFrame);
2190 myId2WindowedDecorator.put(info.getId(), myWindowedDecorator);
2191 myWindowedDecorator.addDisposable(new Disposable() {
2193 public void dispose() {
2194 if (myId2WindowedDecorator.get(info.getId()) != null) {
2195 hideToolWindow(info.getId(), false);
2204 myWindowedDecorator.show(false);
2205 Window window = myWindowedDecorator.getFrame();
2206 JRootPane rootPane = ((RootPaneContainer)window).getRootPane();
2207 Rectangle rootPaneBounds = rootPane.getBounds();
2208 Point point = rootPane.getLocationOnScreen();
2209 Rectangle windowBounds = window.getBounds();
2210 //Point windowLocation = windowBounds.getLocation();
2211 //windowLocation.translate(windowLocation.x - point.x, windowLocation.y - point.y);
2212 window.setLocation(2 * windowBounds.x - point.x, 2 * windowBounds.y - point.y);
2213 window.setSize(2 * windowBounds.width - rootPaneBounds.width, 2 * windowBounds.height - rootPaneBounds.height);
2222 * This command hides and destroys floating decorator for tool window
2223 * with specified <code>ID</code>.
2225 private final class RemoveWindowedDecoratorCmd extends FinalizableCommand {
2226 private final WindowedDecorator myWindowedDecorator;
2228 private RemoveWindowedDecoratorCmd(final WindowInfoImpl info) {
2229 super(myWindowManager.getCommandProcessor());
2230 myWindowedDecorator = getWindowedDecorator(info.getId());
2231 myId2WindowedDecorator.remove(info.getId());
2233 JRootPane rootPane = ((RootPaneContainer)myWindowedDecorator.getFrame()).getRootPane();
2234 if (!rootPane.isShowing()) return;
2235 Rectangle bounds = rootPane.getBounds();
2236 Point location = rootPane.getLocationOnScreen();
2237 bounds.setBounds(location.x - DIVIDER_WIDTH, location.y - DIVIDER_WIDTH, bounds.width + 2 * DIVIDER_WIDTH, bounds.height + 2 * DIVIDER_WIDTH);
2238 info.setFloatingBounds(bounds);
2244 Disposer.dispose(myWindowedDecorator);
2253 public Condition getExpireCondition() {
2254 return ApplicationManager.getApplication().getDisposed();
2258 private final class EditorComponentFocusWatcher extends FocusWatcher {
2260 protected void focusedComponentChanged(final Component component, final AWTEvent cause) {
2261 if (myWindowManager.getCommandProcessor().getCommandCount() > 0 || component == null) {
2264 final KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
2265 final Component owner = mgr.getFocusOwner();
2267 if (owner instanceof EditorComponentImpl && cause instanceof FocusEvent) {
2268 JFrame frame = WindowManager.getInstance().getFrame(myProject);
2269 Component oppositeComponent = ((FocusEvent)cause).getOppositeComponent();
2270 if (oppositeComponent != null && UIUtil.getWindow(oppositeComponent) != frame) {
2275 IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new ExpirableRunnable.ForProject(myProject) {
2278 if (mgr.getFocusOwner() == owner) {
2279 activateEditorComponent(false);
2288 * Notifies window manager about focus traversal in tool window
2290 private final class ToolWindowFocusWatcher extends FocusWatcher {
2291 private final String myId;
2292 private final ToolWindowImpl myToolWindow;
2295 private ToolWindowFocusWatcher(final ToolWindowImpl toolWindow) {
2296 myId = toolWindow.getId();
2297 install(toolWindow.getComponent());
2298 myToolWindow = toolWindow;
2301 public void deinstall() {
2302 deinstall(myToolWindow.getComponent());
2306 protected boolean isFocusedComponentChangeValid(final Component comp, final AWTEvent cause) {
2307 return myWindowManager.getCommandProcessor().getCommandCount() == 0 && comp != null;
2311 protected void focusedComponentChanged(final Component component, final AWTEvent cause) {
2312 if (myWindowManager.getCommandProcessor().getCommandCount() > 0 || component == null) {
2315 final WindowInfoImpl info = getInfo(myId);
2316 //getFocusManagerImpl(myProject)..cancelAllRequests();
2318 if (!info.isActive()) {
2319 getFocusManagerImpl(myProject).doWhenFocusSettlesDown(new EdtRunnable() {
2321 public void runEdt() {
2322 WindowInfoImpl windowInfo = myLayout.getInfo(myId, true);
2323 if (windowInfo == null || !windowInfo.isVisible()) return;
2324 activateToolWindow(myId, false, false);
2332 * Spies on IdeToolWindow properties and applies them to the window
2335 private final class MyToolWindowPropertyChangeListener implements PropertyChangeListener {
2337 public void propertyChange(final PropertyChangeEvent e) {
2338 ToolWindowImpl toolWindow = (ToolWindowImpl)e.getSource();
2339 if (ToolWindowEx.PROP_AVAILABLE.equals(e.getPropertyName())) {
2340 final WindowInfoImpl info = getInfo(toolWindow.getId());
2341 if (!toolWindow.isAvailable() && info.isVisible()) {
2342 hideToolWindow(toolWindow.getId(), false);
2345 StripeButton button = myId2StripeButton.get(toolWindow.getId());
2346 if (button != null) button.updatePresentation();
2347 ActivateToolWindowAction.updateToolWindowActionPresentation(toolWindow);
2352 * Translates events from InternalDecorator into ToolWindowManager method invocations.
2354 private final class MyInternalDecoratorListener implements InternalDecoratorListener {
2356 public void anchorChanged(final InternalDecorator source, final ToolWindowAnchor anchor) {
2357 setToolWindowAnchor(source.getToolWindow().getId(), anchor);
2361 public void autoHideChanged(final InternalDecorator source, final boolean autoHide) {
2362 setToolWindowAutoHide(source.getToolWindow().getId(), autoHide);
2366 public void hidden(final InternalDecorator source) {
2367 hideToolWindow(source.getToolWindow().getId(), false);
2371 public void hiddenSide(final InternalDecorator source) {
2372 hideToolWindow(source.getToolWindow().getId(), true);
2376 public void contentUiTypeChanges(InternalDecorator source, @NotNull ToolWindowContentUiType type) {
2377 setContentUiType(source.getToolWindow().getId(), type);
2381 * Handles event from decorator and modify weight/floating bounds of the
2382 * tool window depending on decoration type.
2385 public void resized(final InternalDecorator source) {
2386 if (!source.isShowing()) {
2387 return; // do not recalculate the tool window size if it is not yet shown (and, therefore, has 0,0,0,0 bounds)
2390 final WindowInfoImpl info = getInfo(source.getToolWindow().getId());
2391 InternalDecorator another = null;
2392 if (info.isFloating()) {
2393 final Window owner = SwingUtilities.getWindowAncestor(source);
2394 if (owner != null) {
2395 info.setFloatingBounds(owner.getBounds());
2398 else { // docked and sliding windows
2399 ToolWindowAnchor anchor = info.getAnchor();
2400 if (source.getParent() instanceof Splitter) {
2401 float sizeInSplit = anchor.isSplitVertically() ? source.getHeight() : source.getWidth();
2402 Splitter splitter = (Splitter)source.getParent();
2403 if (splitter.getSecondComponent() == source) {
2404 sizeInSplit += splitter.getDividerWidth();
2405 another = (InternalDecorator)splitter.getFirstComponent();
2408 another = (InternalDecorator)splitter.getSecondComponent();
2410 if (anchor.isSplitVertically()) {
2411 info.setSideWeight(sizeInSplit / (float)splitter.getHeight());
2414 info.setSideWeight(sizeInSplit / (float)splitter.getWidth());
2418 float paneWeight = anchor.isHorizontal()
2419 ? (float)source.getHeight() / (float)myToolWindowsPane.getMyLayeredPane().getHeight()
2420 : (float)source.getWidth() / (float)myToolWindowsPane.getMyLayeredPane().getWidth();
2421 info.setWeight(paneWeight);
2422 if (another != null && anchor.isSplitVertically()) {
2423 paneWeight = anchor.isHorizontal()
2424 ? (float)another.getHeight() / (float)myToolWindowsPane.getMyLayeredPane().getHeight()
2425 : (float)another.getWidth() / (float)myToolWindowsPane.getMyLayeredPane().getWidth();
2426 another.getWindowInfo().setWeight(paneWeight);
2432 public void activated(final InternalDecorator source) {
2433 activateToolWindow(source.getToolWindow().getId(), true, true);
2437 public void typeChanged(final InternalDecorator source, final ToolWindowType type) {
2438 setToolWindowType(source.getToolWindow().getId(), type);
2442 public void sideStatusChanged(final InternalDecorator source, final boolean isSideTool) {
2443 setSideTool(source.getToolWindow().getId(), isSideTool);
2446 public void visibleStripeButtonChanged(InternalDecorator source, boolean visible) {
2447 setShowStripeButton(source.getToolWindow().getId(), visible);
2451 private void updateComponentTreeUI() {
2452 ApplicationManager.getApplication().assertIsDispatchThread();
2453 final WindowInfoImpl[] infos = myLayout.getInfos();
2454 for (WindowInfoImpl info : infos) {
2455 // the main goal is to update hidden TW components because they are not in the hierarchy
2456 // and will not be updated automatically but unfortunately the visibility of a TW may change
2457 // during the same actionPerformed() so we can't optimize and have to process all of them
2458 IJSwingUtilities.updateComponentTreeUI(getInternalDecorator(info.getId()));
2462 private final class MyUIManagerPropertyChangeListener implements PropertyChangeListener {
2464 public void propertyChange(final PropertyChangeEvent e) {
2465 updateComponentTreeUI();
2469 private final class MyLafManagerListener implements LafManagerListener {
2471 public void lookAndFeelChanged(final LafManager source) {
2472 updateComponentTreeUI();
2479 public String getComponentName() {
2480 return "ToolWindowManager";
2484 public ActionCallback requestDefaultFocus(final boolean forced) {
2485 return getFocusManagerImpl(myProject).requestFocus(new FocusCommand() {
2488 public ActionCallback run() {
2489 return processDefaultFocusRequest(forced);
2494 private void focusToolWinowByDefault(@Nullable String idToIngore) {
2495 String toFocus = null;
2497 for (String each : myActiveStack.getStack()) {
2498 if (idToIngore != null && idToIngore.equalsIgnoreCase(each)) continue;
2500 if (getInfo(each).isVisible()) {
2506 if (toFocus == null) {
2507 for (String each : myActiveStack.getPersistentStack()) {
2508 if (idToIngore != null && idToIngore.equalsIgnoreCase(each)) continue;
2510 if (getInfo(each).isVisible()) {
2517 if (toFocus != null) {
2518 activateToolWindow(toFocus, false, true);
2522 private ActionCallback processDefaultFocusRequest(boolean forced) {
2523 if (ModalityState.NON_MODAL.equals(ModalityState.current())) {
2524 final String activeId = getActiveToolWindowId();
2525 if (isEditorComponentActive() || activeId == null || getToolWindow(activeId) == null) {
2526 activateEditorComponent(forced, true);
2529 activateToolWindow(activeId, forced, true);
2532 return ActionCallback.DONE;
2534 Window activeWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
2535 if (activeWindow != null) {
2536 JRootPane root = null;
2537 if (activeWindow instanceof JDialog) {
2538 root = ((JDialog)activeWindow).getRootPane();
2540 else if (activeWindow instanceof JFrame) {
2541 root = ((JFrame)activeWindow).getRootPane();
2545 JComponent toFocus = IdeFocusTraversalPolicy.getPreferredFocusedComponent(root);
2546 if (toFocus != null) {
2547 if (DialogWrapper.findInstance(toFocus) != null) {
2548 return ActionCallback.DONE; //IDEA-80929
2550 return IdeFocusManager.findInstanceByComponent(toFocus).requestFocus(toFocus, forced);
2554 return ActionCallback.REJECTED;
2559 * Delegate method for compatibility with older versions of IDEA
2562 public ActionCallback requestFocus(@NotNull Component c, boolean forced) {
2563 return IdeFocusManager.getInstance(myProject).requestFocus(c, forced);
2567 public ActionCallback requestFocus(@NotNull FocusCommand command, boolean forced) {
2568 return IdeFocusManager.getInstance(myProject).requestFocus(command, forced);
2571 public void doWhenFocusSettlesDown(@NotNull Runnable runnable) {
2572 IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(runnable);
2575 public boolean dispatch(@NotNull KeyEvent e) {
2576 return IdeFocusManager.getInstance(myProject).dispatch(e);
2579 public Expirable getTimestamp(boolean trackOnlyForcedCommands) {
2580 return IdeFocusManager.getInstance(myProject).getTimestamp(trackOnlyForcedCommands);
2583 public void setShowStripeButton(String id, boolean visibleOnPanel) {
2585 WindowInfoImpl info = getInfo(id);
2586 if (visibleOnPanel == info.isShowStripeButton()) {
2589 info.setShowStripeButton(visibleOnPanel);
2590 UsageTrigger.trigger("StripeButton[" + id + "]." + (visibleOnPanel ? "shown" : "hidden"));
2592 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
2593 appendApplyWindowInfoCmd(info, commandList);
2594 execute(commandList);
2597 public boolean isShowStripeButton(String id) {
2598 WindowInfoImpl info = getInfo(id);
2599 return info == null || info.isShowStripeButton();