toolwindow resize for docked windows
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / ToolWindowManagerImpl.java
blobed63380ac3fa96db139d9d7f7d9c05fc9fc3a4e2
1 /*
2 * Copyright 2000-2009 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.Patches;
19 import com.intellij.ide.IdeEventQueue;
20 import com.intellij.ide.ui.LafManager;
21 import com.intellij.ide.ui.LafManagerListener;
22 import com.intellij.openapi.Disposable;
23 import com.intellij.openapi.application.Application;
24 import com.intellij.openapi.application.ApplicationAdapter;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.application.ModalityState;
27 import com.intellij.openapi.components.ProjectComponent;
28 import com.intellij.openapi.diagnostic.Logger;
29 import com.intellij.openapi.extensions.Extensions;
30 import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
31 import com.intellij.openapi.project.DumbAware;
32 import com.intellij.openapi.project.DumbAwareRunnable;
33 import com.intellij.openapi.project.DumbService;
34 import com.intellij.openapi.project.Project;
35 import com.intellij.openapi.startup.StartupManager;
36 import com.intellij.openapi.ui.MessageType;
37 import com.intellij.openapi.ui.popup.Balloon;
38 import com.intellij.openapi.ui.popup.JBPopupFactory;
39 import com.intellij.openapi.util.*;
40 import com.intellij.openapi.util.registry.Registry;
41 import com.intellij.openapi.wm.*;
42 import com.intellij.openapi.wm.ex.ToolWindowEx;
43 import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
44 import com.intellij.openapi.wm.ex.ToolWindowManagerListener;
45 import com.intellij.openapi.wm.ex.WindowManagerEx;
46 import com.intellij.openapi.wm.impl.commands.*;
47 import com.intellij.ui.awt.RelativePoint;
48 import com.intellij.util.Alarm;
49 import com.intellij.util.ArrayUtil;
50 import com.intellij.util.containers.CollectionFactory;
51 import com.intellij.util.containers.HashMap;
52 import com.intellij.util.containers.HashSet;
53 import com.intellij.util.ui.UIUtil;
54 import com.intellij.util.ui.update.UiNotifyConnector;
55 import org.jdom.Element;
56 import org.jetbrains.annotations.NonNls;
57 import org.jetbrains.annotations.NotNull;
58 import org.jetbrains.annotations.Nullable;
60 import javax.swing.*;
61 import javax.swing.event.EventListenerList;
62 import javax.swing.event.HyperlinkListener;
63 import java.awt.*;
64 import java.awt.event.FocusEvent;
65 import java.awt.event.KeyEvent;
66 import java.beans.PropertyChangeEvent;
67 import java.beans.PropertyChangeListener;
68 import java.lang.ref.WeakReference;
69 import java.util.*;
70 import java.util.List;
72 /**
73 * @author Anton Katilin
74 * @author Vladimir Kondratyev
76 public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements ProjectComponent, JDOMExternalizable {
77 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.ToolWindowManagerImpl");
79 private final Project myProject;
80 private final WindowManagerEx myWindowManager;
81 private final EventListenerList myListenerList;
82 private final DesktopLayout myLayout;
83 private final HashMap<String, InternalDecorator> myId2InternalDecorator;
84 private final HashMap<String, FloatingDecorator> myId2FloatingDecorator;
85 private final HashMap<String, StripeButton> myId2StripeButton;
86 private final HashMap<String, FocusWatcher> myId2FocusWatcher;
87 private final Set<String> myDumbAwareIds = Collections.synchronizedSet(CollectionFactory.<String>newTroveSet());
89 private final EditorComponentFocusWatcher myEditorComponentFocusWatcher;
90 private final MyToolWindowPropertyChangeListener myToolWindowPropertyChangeListener;
91 private final InternalDecoratorListener myInternalDecoratorListener;
93 private boolean myEditorComponentActive;
94 private final ActiveStack myActiveStack;
95 private final SideStack mySideStack;
97 private ToolWindowsPane myToolWindowsPane;
98 private IdeFrameImpl myFrame;
99 private DesktopLayout myLayoutToRestoreLater = null;
100 @NonNls private static final String EDITOR_ELEMENT = "editor";
101 @NonNls private static final String ACTIVE_ATTR_VALUE = "active";
102 @NonNls private static final String FRAME_ELEMENT = "frame";
103 @NonNls private static final String X_ATTR = "x";
104 @NonNls private static final String Y_ATTR = "y";
105 @NonNls private static final String WIDTH_ATTR = "width";
106 @NonNls private static final String HEIGHT_ATTR = "height";
107 @NonNls private static final String EXTENDED_STATE_ATTR = "extended-state";
109 private final EdtAlarm myFocusedComponentAlaram;
110 private final EdtAlarm myForcedFocusRequestsAlarm;
112 private final EdtAlarm myIdleAlarm;
113 private final Set<Runnable> myIdleRequests = new HashSet<Runnable>();
114 private final EdtRunnable myIdleRunnable = new EdtRunnable() {
115 public void runEdt() {
116 if (isFocusTransferReady() && !isIdleQueueEmpty()) {
117 flushIdleRequests();
119 else {
120 restartIdleAlarm();
125 private FocusCommand myRequestFocusCmd;
126 private final ArrayList<FocusCommand> myFocusRequests = new ArrayList<FocusCommand>();
128 private final ArrayList<KeyEvent> myToDispatchOnDone = new ArrayList<KeyEvent>();
129 private int myFlushingIdleRequestsEntryCount = 0;
131 private WeakReference<FocusCommand> myLastForcedRequest = new WeakReference<FocusCommand>(null);
133 private FocusCommand myFocusCommandOnAppActivation;
134 private ActionCallback myCallbackOnActivation;
135 private WeakReference<Component> myFocusedComponentOnDeactivation;
136 private WeakReference<Component> myLastFocusedProjectComponent;
138 private final IdeEventQueue myQueue;
139 private final KeyProcessorConext myKeyProcessorContext = new KeyProcessorConext();
141 private long myCmdTimestamp;
142 private long myForcedCmdTimestamp;
143 private final Application myApp;
145 private Set<String> myRestoredToolWindowIds = new java.util.HashSet<String>();
148 * invoked by reflection
150 public ToolWindowManagerImpl(final Project project, WindowManagerEx windowManagerEx, final Application app) {
151 myApp = app;
152 myQueue = IdeEventQueue.getInstance();
153 myProject = project;
154 myWindowManager = windowManagerEx;
155 myListenerList = new EventListenerList();
157 myLayout = new DesktopLayout();
158 myLayout.copyFrom(windowManagerEx.getLayout());
160 myId2InternalDecorator = new HashMap<String, InternalDecorator>();
161 myId2FloatingDecorator = new HashMap<String, FloatingDecorator>();
162 myId2StripeButton = new HashMap<String, StripeButton>();
163 myId2FocusWatcher = new HashMap<String, FocusWatcher>();
165 myEditorComponentFocusWatcher = new EditorComponentFocusWatcher();
166 myToolWindowPropertyChangeListener = new MyToolWindowPropertyChangeListener();
167 myInternalDecoratorListener = new MyInternalDecoratorListener();
169 myEditorComponentActive = false;
170 myActiveStack = new ActiveStack();
171 mySideStack = new SideStack();
173 myFocusedComponentAlaram = new EdtAlarm(project);
174 myForcedFocusRequestsAlarm = new EdtAlarm(project);
175 myIdleAlarm = new EdtAlarm(project);
177 final AppListener myAppListener = new AppListener();
178 app.addApplicationListener(myAppListener);
179 Disposer.register(project, new Disposable() {
180 public void dispose() {
181 app.removeApplicationListener(myAppListener);
185 IdeEventQueue.getInstance().addDispatcher(new IdeEventQueue.EventDispatcher() {
186 public boolean dispatch(AWTEvent e) {
187 if (e instanceof FocusEvent) {
188 final FocusEvent fe = (FocusEvent)e;
189 final Component c = fe.getComponent();
190 final IdeFrameImpl frame = myWindowManager.getFrame(myProject);
191 if (c instanceof Window || c == null || frame == null) return false;
192 if (isProjectComponent(c)) {
193 if (fe.getID() == FocusEvent.FOCUS_GAINED) {
194 myLastFocusedProjectComponent = new WeakReference<Component>(c);
199 return false;
201 }, myProject);
204 private Component getLastFocusedProjectComponent() {
205 return myLastFocusedProjectComponent != null ? myLastFocusedProjectComponent.get() : null;
208 public Project getProject() {
209 return myProject;
212 public void initComponent() {
215 public void disposeComponent() {
218 public void projectOpened() {
219 final MyUIManagerPropertyChangeListener myUIManagerPropertyChangeListener = new MyUIManagerPropertyChangeListener();
220 final MyLafManagerListener myLafManagerListener = new MyLafManagerListener();
222 UIManager.addPropertyChangeListener(myUIManagerPropertyChangeListener);
223 LafManager.getInstance().addLafManagerListener(myLafManagerListener);
225 Disposer.register(myProject, new Disposable() {
226 public void dispose() {
227 UIManager.removePropertyChangeListener(myUIManagerPropertyChangeListener);
228 LafManager.getInstance().removeLafManagerListener(myLafManagerListener);
231 myFrame = myWindowManager.allocateFrame(myProject);
232 LOG.assertTrue(myFrame != null);
234 final ArrayList<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
236 myToolWindowsPane = new ToolWindowsPane(myFrame, this);
237 ((IdeRootPane)myFrame.getRootPane()).setToolWindowsPane(myToolWindowsPane);
238 appendUpdateToolWindowsPaneCmd(commandsList);
240 myFrame.setTitle(FrameTitleBuilder.getInstance().getProjectTitle(myProject));
242 final JComponent editorComponent = FileEditorManagerEx.getInstanceEx(myProject).getComponent();
243 myEditorComponentFocusWatcher.install(editorComponent);
245 appendSetEditorComponentCmd(editorComponent, commandsList);
246 if (myEditorComponentActive) {
247 activateEditorComponentImpl(commandsList, true);
249 execute(commandsList);
251 final DumbService.DumbModeListener dumbModeListener = new DumbService.DumbModeListener() {
252 public void enteredDumbMode() {
253 for (final String id : getToolWindowIds()) {
254 if (!myDumbAwareIds.contains(id)) {
255 if (isToolWindowVisible(id)) {
256 hideToolWindow(id, true);
258 getStripeButton(id).setEnabled(false);
263 public void exitDumbMode() {
264 for (final String id : getToolWindowIds()) {
265 getStripeButton(id).setEnabled(true);
269 myProject.getMessageBus().connect().subscribe(DumbService.DUMB_MODE, dumbModeListener);
271 StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() {
272 public void run() {
273 registerToolWindowsFromBeans();
274 if (DumbService.getInstance(myProject).isDumb()) {
275 dumbModeListener.enteredDumbMode();
281 private void registerToolWindowsFromBeans() {
282 ToolWindowEP[] beans = Extensions.getExtensions(ToolWindowEP.EP_NAME);
283 for (final ToolWindowEP bean : beans) {
284 final Condition condition = bean.getCondition();
285 if (condition != null && !condition.value(myProject)) {
286 continue;
288 ToolWindowAnchor toolWindowAnchor;
289 try {
290 toolWindowAnchor = ToolWindowAnchor.fromText(bean.anchor);
292 catch (Exception e) {
293 LOG.error(e);
294 continue;
296 JLabel label = new JLabel("Initializing...", JLabel.CENTER);
297 label.setOpaque(true);
298 final Color treeBg = UIManager.getColor("Tree.background");
299 label.setBackground(new Color(treeBg.getRed(), treeBg.getGreen(), treeBg.getBlue(), 180));
300 final Color treeFg = UIManager.getColor("Tree.foreground");
301 label.setForeground(new Color(treeFg.getRed(), treeFg.getGreen(), treeFg.getBlue(), 180));
302 final ToolWindowFactory factory = bean.getToolWindowFactory();
303 final ToolWindowImpl toolWindow = (ToolWindowImpl)registerToolWindow(bean.id, label, toolWindowAnchor, myProject, factory instanceof DumbAware);
304 toolWindow.setContentFactory(factory);
305 if (bean.icon != null) {
306 Icon icon = IconLoader.findIcon(bean.icon, factory.getClass());
307 if (icon == null) {
308 try {
309 icon = IconLoader.getIcon(bean.icon);
310 } catch (Exception ignored) {}
312 toolWindow.setIcon(icon);
315 if (!getInfo(bean.id).isSplit() && bean.secondary && !myRestoredToolWindowIds.contains(bean.id)) {
316 toolWindow.setSplitMode(bean.secondary, null);
319 final ActionCallback activation = toolWindow.setActivation(new ActionCallback());
321 UiNotifyConnector.doWhenFirstShown(label, new Runnable() {
322 public void run() {
323 ApplicationManager.getApplication().invokeLater(new DumbAwareRunnable() {
324 public void run() {
325 toolWindow.ensureContentInitialized();
326 activation.setDone();
334 public void projectClosed() {
335 final ArrayList<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
336 final String[] ids = getToolWindowIds();
338 // Remove ToolWindowsPane
340 ((IdeRootPane)myFrame.getRootPane()).setToolWindowsPane(null);
341 myWindowManager.releaseFrame(myFrame);
342 appendUpdateToolWindowsPaneCmd(commandsList);
344 // Hide all tool windows
346 for (final String id : ids) {
347 deactivateToolWindowImpl(id, true, commandsList);
350 // Remove editor component
352 final JComponent editorComponent = FileEditorManagerEx.getInstanceEx(myProject).getComponent();
353 myEditorComponentFocusWatcher.deinstall(editorComponent);
354 appendSetEditorComponentCmd(null, commandsList);
355 execute(commandsList);
358 public void addToolWindowManagerListener(final ToolWindowManagerListener l) {
359 myListenerList.add(ToolWindowManagerListener.class, l);
362 public void removeToolWindowManagerListener(final ToolWindowManagerListener l) {
363 myListenerList.remove(ToolWindowManagerListener.class, l);
367 * This is helper method. It delegated its fuctionality to the WindowManager.
368 * Before delegating it fires state changed.
370 private void execute(final ArrayList<FinalizableCommand> commandList) {
371 for (FinalizableCommand each : commandList) {
372 if (each.willChangeState()) {
373 fireStateChanged();
374 break;
378 for (FinalizableCommand each : commandList) {
379 each.beforeExecute(this);
381 myWindowManager.getCommandProcessor().execute(commandList, myProject.getDisposed());
384 public void activateEditorComponent() {
385 activateEditorComponent(true);
388 private void activateEditorComponent(boolean forced) {
389 if (LOG.isDebugEnabled()) {
390 LOG.debug("enter: activateEditorComponent()");
392 ApplicationManager.getApplication().assertIsDispatchThread();
393 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
394 activateEditorComponentImpl(commandList, forced);
395 execute(commandList);
398 private void activateEditorComponentImpl(final ArrayList<FinalizableCommand> commandList, final boolean forced) {
399 final String active = getActiveToolWindowId();
400 // Now we have to request focus into most recent focused editor
401 appendRequestFocusInEditorComponentCmd(commandList, forced).doWhenDone(new Runnable() {
402 public void run() {
403 final ArrayList<FinalizableCommand> postExecute = new ArrayList<FinalizableCommand>();
405 if (LOG.isDebugEnabled()) {
406 LOG.debug("editor activated");
408 deactivateWindows(postExecute, null);
409 myActiveStack.clear();
410 myEditorComponentActive = true;
412 execute(postExecute);
414 }).doWhenRejected(new Runnable() {
415 public void run() {
416 if (forced) {
417 requestFocus(new FocusCommand() {
418 public ActionCallback run() {
419 final ArrayList<FinalizableCommand> cmds = new ArrayList<FinalizableCommand>();
421 final WindowInfoImpl toReactivate = getInfo(active);
422 final boolean reactivateLastActive = toReactivate != null && !isToHide(toReactivate);
423 deactivateWindows(cmds, reactivateLastActive ? active : null);
424 execute(cmds);
426 if (reactivateLastActive) {
427 activateToolWindow(active, false, true);
429 else {
430 if (active != null) {
431 myActiveStack.remove(active, false);
434 if (!myActiveStack.isEmpty()) {
435 activateToolWindow(myActiveStack.peek(), false, true);
438 return new ActionCallback.Done();
440 }, false);
446 private void deactivateWindows(final ArrayList<FinalizableCommand> postExecute, String idToIgnore) {
447 final WindowInfoImpl[] infos = myLayout.getInfos();
448 for (final WindowInfoImpl info : infos) {
449 final boolean shouldHide = isToHide(info);
450 if (idToIgnore != null && idToIgnore.equals(info.getId())) {
451 continue;
453 deactivateToolWindowImpl(info.getId(), shouldHide, postExecute);
457 private boolean isToHide(final WindowInfoImpl info) {
458 return (info.isAutoHide() || info.isSliding()) && !(info.isFloating() && hasModalChild(info));
462 * Helper method. It makes window visible, activates it and request focus into the tool window.
463 * But it doesn't deactivate other tool windows. Use <code>prepareForActivation</code> method to
464 * deactivates other tool windows.
466 * @param dirtyMode if <code>true</code> then all UI operations are performed in "dirty" mode.
467 * It means that UI isn't validated and repainted just after each add/remove operation.
468 * @see ToolWindowManagerImpl#prepareForActivation
470 private void showAndActivate(final String id,
471 final boolean dirtyMode,
472 final ArrayList<FinalizableCommand> commandsList,
473 boolean autoFocusContents) {
474 if (!getToolWindow(id).isAvailable()) {
475 return;
477 // show activated
478 final WindowInfoImpl info = getInfo(id);
479 boolean toApplyInfo = false;
480 if (!info.isActive()) {
481 info.setActive(true);
482 toApplyInfo = true;
484 showToolWindowImpl(id, dirtyMode, commandsList);
486 // activate
487 if (toApplyInfo) {
488 appendApplyWindowInfoCmd(info, commandsList);
489 myActiveStack.push(id);
490 myEditorComponentActive = false;
493 if (autoFocusContents) {
494 appendRequestFocusInToolWindowCmd(id, commandsList, true);
498 void activateToolWindow(final String id, boolean forced, boolean autoFocusContents) {
499 if (LOG.isDebugEnabled()) {
500 LOG.debug("enter: activateToolWindow(" + id + ")");
502 ApplicationManager.getApplication().assertIsDispatchThread();
503 checkId(id);
504 if (DumbService.getInstance(myProject).isDumb() && !myDumbAwareIds.contains(id)) {
505 return;
508 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
509 activateToolWindowImpl(id, commandList, forced, autoFocusContents);
510 execute(commandList);
513 private void activateToolWindowImpl(final String id,
514 final ArrayList<FinalizableCommand> commandList,
515 boolean forced,
516 boolean autoFocusContents) {
517 if (!isUnforcedRequestAllowed() && !forced) return;
519 if (LOG.isDebugEnabled()) {
520 LOG.debug("enter: activateToolWindowImpl(" + id + ")");
522 if (!getToolWindow(id).isAvailable()) {
523 // Tool window can be "logically" active but not focused. For example,
524 // when the user switched to another application. So we just need to bring
525 // tool window's window to front.
526 final InternalDecorator decorator = getInternalDecorator(id);
527 if (!decorator.hasFocus() && autoFocusContents) {
528 appendRequestFocusInToolWindowCmd(id, commandList, forced);
530 return;
532 prepareForActivation(id, commandList);
533 showAndActivate(id, false, commandList, autoFocusContents);
537 * Checkes whether the specified <code>id</code> defines installed tool
538 * window. If it's not then throws <code>IllegalStateException</code>.
540 * @throws IllegalStateException if tool window isn't installed.
542 private void checkId(final String id) {
543 if (!myLayout.isToolWindowRegistered(id)) {
544 throw new IllegalStateException("window with id=\"" + id + "\" isn't registered");
549 * Helper method. It deactivates (and hides) window with specified <code>id</code>.
551 * @param id <code>id</code> of the tool window to be deactivated.
552 * @param shouldHide if <code>true</code> then also hides specified tool window.
554 private void deactivateToolWindowImpl(final String id, final boolean shouldHide, final List<FinalizableCommand> commandsList) {
555 if (LOG.isDebugEnabled()) {
556 LOG.debug("enter: deactivateToolWindowImpl(" + id + "," + shouldHide + ")");
558 final WindowInfoImpl info = getInfo(id);
559 if (shouldHide && info.isVisible()) {
560 info.setVisible(false);
561 if (info.isFloating()) {
562 appendRemoveFloatingDecoratorCmd(info, commandsList);
564 else { // docked and sliding windows
565 appendRemoveDecoratorCmd(id, false, commandsList);
568 info.setActive(false);
569 appendApplyWindowInfoCmd(info, commandsList);
572 public String[] getToolWindowIds() {
573 ApplicationManager.getApplication().assertIsDispatchThread();
574 final WindowInfoImpl[] infos = myLayout.getInfos();
575 final String[] ids = ArrayUtil.newStringArray(infos.length);
576 for (int i = 0; i < infos.length; i++) {
577 ids[i] = infos[i].getId();
579 return ids;
582 public String getActiveToolWindowId() {
583 ApplicationManager.getApplication().assertIsDispatchThread();
584 return myLayout.getActiveId();
587 public String getLastActiveToolWindowId() {
588 return getLastActiveToolWindowId(null);
591 public String getLastActiveToolWindowId(Condition<JComponent> condition) {
592 ApplicationManager.getApplication().assertIsDispatchThread();
593 String lastActiveToolWindowId = null;
594 for (int i = 0; i < myActiveStack.getPersistentSize(); i++) {
595 final String id = myActiveStack.peekPersistent(i);
596 final ToolWindow toolWindow = getToolWindow(id);
597 LOG.assertTrue(toolWindow != null);
598 if (toolWindow.isAvailable()) {
599 if (condition == null || condition.value(toolWindow.getComponent())) {
600 lastActiveToolWindowId = id;
601 break;
605 return lastActiveToolWindowId;
609 * @return floating decorator for the tool window with specified <code>ID</code>.
611 private FloatingDecorator getFloatingDecorator(final String id) {
612 return myId2FloatingDecorator.get(id);
616 * @return internal decorator for the tool window with specified <code>ID</code>.
618 private InternalDecorator getInternalDecorator(final String id) {
619 return myId2InternalDecorator.get(id);
623 * @return tool button for the window with specified <code>ID</code>.
625 private StripeButton getStripeButton(final String id) {
626 return myId2StripeButton.get(id);
630 * @return info for the tool window with specified <code>ID</code>.
632 private WindowInfoImpl getInfo(final String id) {
633 return myLayout.getInfo(id, true);
636 public List<String> getIdsOn(final ToolWindowAnchor anchor) {
637 return myLayout.getVisibleIdsOn(anchor, this);
640 public ToolWindow getToolWindow(final String id) {
641 ApplicationManager.getApplication().assertIsDispatchThread();
642 if (!myLayout.isToolWindowRegistered(id)) {
643 return null;
645 return getInternalDecorator(id).getToolWindow();
648 void showToolWindow(final String id) {
649 if (LOG.isDebugEnabled()) {
650 LOG.debug("enter: showToolWindow(" + id + ")");
652 ApplicationManager.getApplication().assertIsDispatchThread();
653 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
654 showToolWindowImpl(id, false, commandList);
655 execute(commandList);
658 public void hideToolWindow(final String id, final boolean hideSide) {
659 hideToolWindow(id, hideSide, true);
662 public void hideToolWindow(final String id, final boolean hideSide, final boolean moveFocus) {
663 ApplicationManager.getApplication().assertIsDispatchThread();
664 checkId(id);
665 final WindowInfoImpl info = getInfo(id);
666 if (!info.isVisible()) return;
667 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
668 final boolean wasActive = info.isActive();
670 // hide and deactivate
672 deactivateToolWindowImpl(id, true, commandList);
674 if (hideSide || info.isFloating()) {
675 final List<String> ids = myLayout.getVisibleIdsOn(info.getAnchor(), this);
676 for (String each : ids) {
677 myActiveStack.remove(each, true);
681 while (!mySideStack.isEmpty(info.getAnchor())) {
682 mySideStack.pop(info.getAnchor());
685 final String[] all = getToolWindowIds();
686 for (String eachId : all) {
687 final WindowInfoImpl eachInfo = getInfo(eachId);
688 if (eachInfo.isVisible() && eachInfo.getAnchor() == info.getAnchor()) {
689 deactivateToolWindowImpl(eachId, true, commandList);
693 activateEditorComponentImpl(commandList, true);
695 else {
697 // first of all we have to find tool window that was located at the same side and
698 // was hidden.
700 WindowInfoImpl info2 = null;
701 while (!mySideStack.isEmpty(info.getAnchor())) {
702 final WindowInfoImpl storedInfo = mySideStack.pop(info.getAnchor());
703 final WindowInfoImpl currentInfo = getInfo(storedInfo.getId());
704 LOG.assertTrue(currentInfo != null);
705 // SideStack contains copies of real WindowInfos. It means that
706 // these stored infos can be invalid. The following loop removes invalid WindowInfos.
707 if (storedInfo.getAnchor() == currentInfo.getAnchor() &&
708 storedInfo.getType() == currentInfo.getType() &&
709 storedInfo.isAutoHide() == currentInfo.isAutoHide()) {
710 info2 = storedInfo;
711 break;
714 if (info2 != null) {
715 showToolWindowImpl(info2.getId(), false, commandList);
718 // If we hide currently active tool window then we should activate the previous
719 // one which is located in the tool window stack.
720 // Activate another tool window if no active tool window exists and
721 // window stack is enabled.
723 myActiveStack.remove(id, false); // hidden window should be at the top of stack
724 if (wasActive && moveFocus) {
725 if (myActiveStack.isEmpty()) {
726 activateEditorComponentImpl(commandList, false);
728 else {
729 final String toBeActivatedId = myActiveStack.pop();
730 if (toBeActivatedId != null) {
731 activateToolWindowImpl(toBeActivatedId, commandList, false, true);
737 execute(commandList);
741 * @param dirtyMode if <code>true</code> then all UI operations are performed in dirty mode.
743 private void showToolWindowImpl(final String id, final boolean dirtyMode, final List<FinalizableCommand> commandsList) {
744 final WindowInfoImpl toBeShownInfo = getInfo(id);
745 if (toBeShownInfo.isVisible() || !getToolWindow(id).isAvailable()) {
746 return;
749 if (DumbService.getInstance(myProject).isDumb() && !myDumbAwareIds.contains(id)) {
750 return;
753 toBeShownInfo.setVisible(true);
754 final InternalDecorator decorator = getInternalDecorator(id);
756 if (toBeShownInfo.isFloating()) {
757 commandsList.add(new AddFloatingDecoratorCmd(decorator, toBeShownInfo));
759 else { // docked and sliding windows
761 // If there is tool window on the same side then we have to hide it, i.e.
762 // clear place for tool window to be shown.
764 // We store WindowInfo of hidden tool window in the SideStack (if the tool window
765 // is docked and not auto-hide one). Therefore it's possible to restore the
766 // hidden tool window when showing tool window will be closed.
768 final WindowInfoImpl[] infos = myLayout.getInfos();
769 for (final WindowInfoImpl info : infos) {
770 if (id.equals(info.getId())) {
771 continue;
773 if (info.isVisible() &&
774 info.getType() == toBeShownInfo.getType() &&
775 info.getAnchor() == toBeShownInfo.getAnchor() &&
776 info.isSplit() == toBeShownInfo.isSplit()) {
777 // hide and deactivate tool window
778 info.setVisible(false);
779 appendRemoveDecoratorCmd(info.getId(), false, commandsList);
780 if (info.isActive()) {
781 info.setActive(false);
783 appendApplyWindowInfoCmd(info, commandsList);
784 // store WindowInfo into the SideStack
785 if (info.isDocked() && !info.isAutoHide()) {
786 mySideStack.push(info);
790 appendAddDecoratorCmd(decorator, toBeShownInfo, dirtyMode, commandsList);
792 // Remove tool window from the SideStack.
794 mySideStack.remove(id);
797 appendApplyWindowInfoCmd(toBeShownInfo, commandsList);
800 public ToolWindow registerToolWindow(@NotNull final String id,
801 @NotNull final JComponent component,
802 @NotNull final ToolWindowAnchor anchor) {
803 return registerToolWindow(id, component, anchor, false);
806 public ToolWindow registerToolWindow(@NotNull final String id,
807 @NotNull JComponent component,
808 @NotNull ToolWindowAnchor anchor,
809 Disposable parentDisposable) {
810 return registerToolWindow(id, component, anchor, parentDisposable, false);
813 public ToolWindow registerToolWindow(@NotNull final String id,
814 @NotNull JComponent component,
815 @NotNull ToolWindowAnchor anchor,
816 Disposable parentDisposable,
817 boolean canWorkInDumbMode) {
818 return registerDisposable(id, parentDisposable, registerToolWindow(id, component, anchor, canWorkInDumbMode));
821 private ToolWindow registerToolWindow(@NotNull final String id,
822 @NotNull final JComponent component,
823 @NotNull final ToolWindowAnchor anchor,
824 boolean canWorkInDumbMode) {
825 return registerToolWindow(id, component, anchor, false, false, canWorkInDumbMode);
828 public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor) {
829 return registerToolWindow(id, null, anchor, false, canCloseContent, false);
832 public ToolWindow registerToolWindow(@NotNull final String id,
833 final boolean canCloseContent,
834 @NotNull final ToolWindowAnchor anchor,
835 final boolean sideTool) {
836 return registerToolWindow(id, null, anchor, sideTool, canCloseContent, false);
840 public ToolWindow registerToolWindow(@NotNull final String id,
841 final boolean canCloseContent,
842 @NotNull final ToolWindowAnchor anchor,
843 final Disposable parentDisposable,
844 final boolean canWorkInDumbMode) {
845 return registerDisposable(id, parentDisposable, registerToolWindow(id, null, anchor, false, canCloseContent, canWorkInDumbMode));
848 private ToolWindow registerToolWindow(@NotNull final String id,
849 @Nullable final JComponent component,
850 @NotNull final ToolWindowAnchor anchor,
851 boolean sideTool,
852 boolean canCloseContent,
853 final boolean canWorkInDumbMode) {
854 if (LOG.isDebugEnabled()) {
855 LOG.debug("enter: installToolWindow(" + id + "," + component + "," + anchor + "\")");
857 ApplicationManager.getApplication().assertIsDispatchThread();
858 if (myLayout.isToolWindowRegistered(id)) {
859 throw new IllegalArgumentException("window with id=\"" + id + "\" is already registered");
862 final WindowInfoImpl info = myLayout.register(id, anchor, sideTool);
863 final boolean wasActive = info.isActive();
864 final boolean wasVisible = info.isVisible();
865 info.setActive(false);
866 info.setVisible(false);
868 // Create decorator
870 final ToolWindowImpl toolWindow = new ToolWindowImpl(this, id, canCloseContent, component);
871 final InternalDecorator decorator = new InternalDecorator(myProject, info.copy(), toolWindow);
872 myId2InternalDecorator.put(id, decorator);
873 decorator.addInternalDecoratorListener(myInternalDecoratorListener);
874 toolWindow.addPropertyChangeListener(myToolWindowPropertyChangeListener);
875 myId2FocusWatcher.put(id, new ToolWindowFocusWatcher(toolWindow));
877 // Create and show tool button
879 final StripeButton button = new StripeButton(decorator, myToolWindowsPane);
880 myId2StripeButton.put(id, button);
881 final ArrayList<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
882 appendAddButtonCmd(button, info, commandsList);
884 if (canWorkInDumbMode) {
885 myDumbAwareIds.add(id);
886 } else if (DumbService.getInstance(getProject()).isDumb()) {
887 button.setEnabled(false);
890 // If preloaded info is visible or active then we have to show/activate the installed
891 // tool window. This step has sense only for windows which are not in the autohide
892 // mode. But if tool window was active but its mode doen't allow to activate it again
893 // (for example, tool window is in autohide mode) then we just activate editor component.
895 if (!info.isAutoHide() && (info.isDocked() || info.isFloating())) {
896 if (wasActive) {
897 activateToolWindowImpl(info.getId(), commandsList, true, true);
899 else if (wasVisible) {
900 showToolWindowImpl(info.getId(), false, commandsList);
903 else if (wasActive) { // tool window was active but it cannot be activate again
904 activateEditorComponentImpl(commandsList, true);
907 execute(commandsList);
908 fireToolWindowRegistered(id);
909 return toolWindow;
912 private ToolWindow registerDisposable(final String id, final Disposable parentDisposable, final ToolWindow window) {
913 Disposer.register(parentDisposable, new Disposable() {
914 public void dispose() {
915 unregisterToolWindow(id);
918 return window;
921 public void unregisterToolWindow(@NotNull final String id) {
922 if (LOG.isDebugEnabled()) {
923 LOG.debug("enter: unregisterToolWindow(" + id + ")");
925 ApplicationManager.getApplication().assertIsDispatchThread();
926 if (!myLayout.isToolWindowRegistered(id)) {
927 return;
930 final WindowInfoImpl info = getInfo(id);
931 final ToolWindowEx toolWindow = (ToolWindowEx)getToolWindow(id);
932 // Save recent appearance of tool window
933 myLayout.unregister(id);
934 // Remove decorator and tool button from the screen
935 final ArrayList<FinalizableCommand> commandsList = new ArrayList<FinalizableCommand>();
936 if (info.isVisible()) {
937 info.setVisible(false);
938 if (info.isFloating()) {
939 appendRemoveFloatingDecoratorCmd(info, commandsList);
941 else { // floating and sliding windows
942 appendRemoveDecoratorCmd(id, false, commandsList);
945 appendRemoveButtonCmd(id, commandsList);
946 appendApplyWindowInfoCmd(info, commandsList);
947 execute(commandsList);
948 // Remove all references on tool window and save its last properties
949 toolWindow.removePropertyChangeListener(myToolWindowPropertyChangeListener);
950 myActiveStack.remove(id, true);
951 mySideStack.remove(id);
952 // Destroy stripe button
953 final StripeButton button = getStripeButton(id);
954 button.dispose();
955 myId2StripeButton.remove(id);
957 myId2FocusWatcher.remove(id);
958 // Destroy decorator
959 final InternalDecorator decorator = getInternalDecorator(id);
960 decorator.dispose();
961 decorator.removeInternalDecoratorListener(myInternalDecoratorListener);
962 myId2InternalDecorator.remove(id);
965 public DesktopLayout getLayout() {
966 ApplicationManager.getApplication().assertIsDispatchThread();
967 return myLayout;
970 public void setLayoutToRestoreLater(DesktopLayout layout) {
971 myLayoutToRestoreLater = layout;
974 public DesktopLayout getLayoutToRestoreLater() {
975 return myLayoutToRestoreLater;
978 public void setLayout(final DesktopLayout layout) {
979 ApplicationManager.getApplication().assertIsDispatchThread();
980 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
981 // hide tool window that are invisible in new layout
982 final WindowInfoImpl[] currentInfos = myLayout.getInfos();
983 for (final WindowInfoImpl currentInfo : currentInfos) {
984 final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
985 if (info == null) {
986 continue;
988 if (currentInfo.isVisible() && !info.isVisible()) {
989 deactivateToolWindowImpl(currentInfo.getId(), true, commandList);
992 // change anchor of tool windows
993 for (final WindowInfoImpl currentInfo : currentInfos) {
994 final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
995 if (info == null) {
996 continue;
998 if (currentInfo.getAnchor() != info.getAnchor() || currentInfo.getOrder() != info.getOrder()) {
999 setToolWindowAnchorImpl(currentInfo.getId(), info.getAnchor(), info.getOrder(), commandList);
1002 // change types of tool windows
1003 for (final WindowInfoImpl currentInfo : currentInfos) {
1004 final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1005 if (info == null) {
1006 continue;
1008 if (currentInfo.getType() != info.getType()) {
1009 setToolWindowTypeImpl(currentInfo.getId(), info.getType(), commandList);
1012 // change auto-hide state
1013 for (final WindowInfoImpl currentInfo : currentInfos) {
1014 final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1015 if (info == null) {
1016 continue;
1018 if (currentInfo.isAutoHide() != info.isAutoHide()) {
1019 setToolWindowAutoHideImpl(currentInfo.getId(), info.isAutoHide(), commandList);
1022 // restore visibility
1023 for (final WindowInfoImpl currentInfo : currentInfos) {
1024 final WindowInfoImpl info = layout.getInfo(currentInfo.getId(), false);
1025 if (info == null) {
1026 continue;
1028 if (info.isVisible()) {
1029 showToolWindowImpl(currentInfo.getId(), false, commandList);
1032 // if there is no any active tool window and editor is also inactive
1033 // then activate editor
1034 if (!myEditorComponentActive && getActiveToolWindowId() == null) {
1035 activateEditorComponentImpl(commandList, true);
1037 execute(commandList);
1040 public void invokeLater(final Runnable runnable) {
1041 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1042 commandList.add(new InvokeLaterCmd(runnable, myWindowManager.getCommandProcessor()));
1043 execute(commandList);
1046 public IdeFocusManager getFocusManager() {
1047 return IdeFocusManager.getInstance(myProject);
1050 @Override
1051 public void notifyByBalloon(@NotNull final String toolWindowId, @NotNull final MessageType type, @NotNull final String htmlBody) {
1052 notifyByBalloon(toolWindowId, type, htmlBody, null, null);
1055 public void notifyByBalloon(@NotNull final String toolWindowId,
1056 @NotNull final MessageType type,
1057 @NotNull final String text,
1058 @Nullable final Icon icon,
1059 @Nullable HyperlinkListener listener) {
1060 checkId(toolWindowId);
1062 final Stripe stripe = myToolWindowsPane.getStripeFor(toolWindowId);
1063 final ToolWindowImpl window = getInternalDecorator(toolWindowId).getToolWindow();
1064 if (!window.isAvailable()) {
1065 window.setPlaceholderMode(true);
1066 stripe.updateState();
1067 stripe.revalidate();
1068 stripe.repaint();
1071 final ToolWindowAnchor anchor = getInfo(toolWindowId).getAnchor();
1072 final Ref<Balloon.Position> position = Ref.create(Balloon.Position.below);
1073 if (ToolWindowAnchor.TOP == anchor) {
1074 position.set(Balloon.Position.below);
1076 else if (ToolWindowAnchor.BOTTOM == anchor) {
1077 position.set(Balloon.Position.above);
1079 else if (ToolWindowAnchor.LEFT == anchor) {
1080 position.set(Balloon.Position.atRight);
1082 else if (ToolWindowAnchor.RIGHT == anchor) {
1083 position.set(Balloon.Position.atLeft);
1086 Icon actualIcon = icon != null ? icon : type.getDefaultIcon();
1088 final Balloon balloon =
1089 JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text.replace("\n", "<br>"), actualIcon, type.getPopupBackground(), listener)
1090 .createBalloon();
1091 Disposer.register(balloon, new Disposable() {
1092 public void dispose() {
1093 window.setPlaceholderMode(false);
1094 stripe.updateState();
1095 stripe.revalidate();
1096 stripe.repaint();
1100 final StripeButton button = stripe.getButtonFor(toolWindowId);
1101 if (button == null) return;
1103 final Runnable show = new Runnable() {
1104 public void run() {
1105 if (button.isShowing()) {
1106 final Point point = new Point(button.getBounds().width / 2, button.getHeight() / 2 - 2);
1107 balloon.show(new RelativePoint(button, point), position.get());
1109 else {
1110 final Rectangle bounds = myToolWindowsPane.getBounds();
1111 final Point target = UIUtil.getCenterPoint(bounds, new Dimension(1, 1));
1112 if (ToolWindowAnchor.TOP == anchor) {
1113 target.y = 0;
1115 else if (ToolWindowAnchor.BOTTOM == anchor) {
1116 target.y = bounds.height - 3;
1118 else if (ToolWindowAnchor.LEFT == anchor) {
1119 target.x = 0;
1121 else if (ToolWindowAnchor.RIGHT == anchor) {
1122 target.x = bounds.width;
1125 balloon.show(new RelativePoint(myToolWindowsPane, target), position.get());
1130 if (!button.isValid()) {
1131 SwingUtilities.invokeLater(new Runnable() {
1132 public void run() {
1133 show.run();
1137 else {
1138 show.run();
1143 public boolean isEditorComponentActive() {
1144 ApplicationManager.getApplication().assertIsDispatchThread();
1145 return myEditorComponentActive;
1148 ToolWindowAnchor getToolWindowAnchor(final String id) {
1149 ApplicationManager.getApplication().assertIsDispatchThread();
1150 checkId(id);
1151 return getInfo(id).getAnchor();
1154 void setToolWindowAnchor(final String id, final ToolWindowAnchor anchor) {
1155 ApplicationManager.getApplication().assertIsDispatchThread();
1156 setToolWindowAnchor(id, anchor, -1);
1159 void setToolWindowAnchor(final String id, final ToolWindowAnchor anchor, final int order) {
1160 ApplicationManager.getApplication().assertIsDispatchThread();
1161 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1162 setToolWindowAnchorImpl(id, anchor, order, commandList);
1163 execute(commandList);
1166 private void setToolWindowAnchorImpl(final String id,
1167 final ToolWindowAnchor anchor,
1168 final int order,
1169 final ArrayList<FinalizableCommand> commandsList) {
1170 checkId(id);
1171 final WindowInfoImpl info = getInfo(id);
1172 if (anchor == info.getAnchor() && order == info.getOrder()) {
1173 return;
1175 // if tool window isn't visible or only order number is changed then just remove/add stripe button
1176 if (!info.isVisible() || anchor == info.getAnchor() || info.isFloating()) {
1177 appendRemoveButtonCmd(id, commandsList);
1178 myLayout.setAnchor(id, anchor, order);
1179 // update infos for all window. Actually we have to update only infos affected by
1180 // setAnchor method
1181 final WindowInfoImpl[] infos = myLayout.getInfos();
1182 for (WindowInfoImpl info1 : infos) {
1183 appendApplyWindowInfoCmd(info1, commandsList);
1185 appendAddButtonCmd(getStripeButton(id), info, commandsList);
1187 else { // for docked and sliding windows we have to move buttons and window's decorators
1188 info.setVisible(false);
1189 appendRemoveDecoratorCmd(id, false, commandsList);
1190 appendRemoveButtonCmd(id, commandsList);
1191 myLayout.setAnchor(id, anchor, order);
1192 // update infos for all window. Actually we have to update only infos affected by
1193 // setAnchor method
1194 final WindowInfoImpl[] infos = myLayout.getInfos();
1195 for (WindowInfoImpl info1 : infos) {
1196 appendApplyWindowInfoCmd(info1, commandsList);
1198 appendAddButtonCmd(getStripeButton(id), info, commandsList);
1199 showToolWindowImpl(id, false, commandsList);
1200 if (info.isActive()) {
1201 appendRequestFocusInToolWindowCmd(id, commandsList, true);
1206 boolean isSplitMode(String id) {
1207 ApplicationManager.getApplication().assertIsDispatchThread();
1208 checkId(id);
1209 return getInfo(id).isSplit();
1212 ToolWindowContentUiType getContentUiType(String id) {
1213 ApplicationManager.getApplication().assertIsDispatchThread();
1214 checkId(id);
1215 return getInfo(id).getContentUiType();
1218 void setSideTool(String id, boolean isSide) {
1219 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1220 setSplitModeImpl(id, isSide, commandList);
1221 execute(commandList);
1224 public void setContentUiType(String id, ToolWindowContentUiType type) {
1225 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1226 checkId(id);
1227 WindowInfoImpl info = getInfo(id);
1228 info.setContentUiType(type);
1229 appendApplyWindowInfoCmd(info, commandList);
1230 execute(commandList);
1233 void setSideToolAndAnchor(String id, ToolWindowAnchor anchor, int order, boolean isSide) {
1234 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1235 setToolWindowAnchor(id, anchor, order);
1236 setSplitModeImpl(id, isSide, commandList);
1237 execute(commandList);
1240 private void setSplitModeImpl(final String id, final boolean isSplit, final ArrayList<FinalizableCommand> commandList) {
1241 checkId(id);
1242 final WindowInfoImpl info = getInfo(id);
1243 if (isSplit == info.isSplit()) {
1244 return;
1247 myLayout.setSplitMode(id, isSplit);
1249 boolean wasActive = info.isActive();
1250 if (wasActive) {
1251 deactivateToolWindowImpl(id, true, commandList);
1253 final WindowInfoImpl[] infos = myLayout.getInfos();
1254 for (WindowInfoImpl info1 : infos) {
1255 appendApplyWindowInfoCmd(info1, commandList);
1257 if (wasActive) {
1258 activateToolWindowImpl(id, commandList, true, true);
1260 commandList.add(myToolWindowsPane.createUpdateButtonPositionCmd(id, myWindowManager.getCommandProcessor()));
1263 ToolWindowType getToolWindowInternalType(final String id) {
1264 ApplicationManager.getApplication().assertIsDispatchThread();
1265 checkId(id);
1266 return getInfo(id).getInternalType();
1269 ToolWindowType getToolWindowType(final String id) {
1270 ApplicationManager.getApplication().assertIsDispatchThread();
1271 checkId(id);
1272 return getInfo(id).getType();
1275 private void fireToolWindowRegistered(final String id) {
1276 final ToolWindowManagerListener[] listeners = myListenerList.getListeners(ToolWindowManagerListener.class);
1277 for (ToolWindowManagerListener listener : listeners) {
1278 listener.toolWindowRegistered(id);
1282 private void fireStateChanged() {
1283 final ToolWindowManagerListener[] listeners = myListenerList.getListeners(ToolWindowManagerListener.class);
1284 for (ToolWindowManagerListener listener : listeners) {
1285 listener.stateChanged();
1289 boolean isToolWindowActive(final String id) {
1290 ApplicationManager.getApplication().assertIsDispatchThread();
1291 checkId(id);
1292 return getInfo(id).isActive();
1295 boolean isToolWindowAutoHide(final String id) {
1296 ApplicationManager.getApplication().assertIsDispatchThread();
1297 checkId(id);
1298 return getInfo(id).isAutoHide();
1301 public boolean isToolWindowFloating(final String id) {
1302 ApplicationManager.getApplication().assertIsDispatchThread();
1303 checkId(id);
1304 return getInfo(id).isFloating();
1307 boolean isToolWindowVisible(final String id) {
1308 ApplicationManager.getApplication().assertIsDispatchThread();
1309 checkId(id);
1310 return getInfo(id).isVisible();
1313 void setToolWindowAutoHide(final String id, final boolean autoHide) {
1314 ApplicationManager.getApplication().assertIsDispatchThread();
1315 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1316 setToolWindowAutoHideImpl(id, autoHide, commandList);
1317 execute(commandList);
1320 private void setToolWindowAutoHideImpl(final String id, final boolean autoHide, final ArrayList<FinalizableCommand> commandsList) {
1321 checkId(id);
1322 final WindowInfoImpl info = getInfo(id);
1323 if (info.isAutoHide() == autoHide) {
1324 return;
1326 info.setAutoHide(autoHide);
1327 appendApplyWindowInfoCmd(info, commandsList);
1328 if (info.isVisible()) {
1329 prepareForActivation(id, commandsList);
1330 showAndActivate(id, false, commandsList, true);
1334 void setToolWindowType(final String id, final ToolWindowType type) {
1335 ApplicationManager.getApplication().assertIsDispatchThread();
1336 final ArrayList<FinalizableCommand> commandList = new ArrayList<FinalizableCommand>();
1337 setToolWindowTypeImpl(id, type, commandList);
1338 execute(commandList);
1341 private void setToolWindowTypeImpl(final String id, final ToolWindowType type, final ArrayList<FinalizableCommand> commandsList) {
1342 checkId(id);
1343 final WindowInfoImpl info = getInfo(id);
1344 if (info.getType() == type) {
1345 return;
1347 if (info.isVisible()) {
1348 final boolean dirtyMode = info.isDocked() || info.isSliding();
1349 info.setVisible(false);
1350 if (info.isFloating()) {
1351 appendRemoveFloatingDecoratorCmd(info, commandsList);
1353 else { // docked and sliding windows
1354 appendRemoveDecoratorCmd(id, dirtyMode, commandsList);
1356 info.setType(type);
1357 appendApplyWindowInfoCmd(info, commandsList);
1358 prepareForActivation(id, commandsList);
1359 showAndActivate(id, dirtyMode, commandsList, true);
1360 appendUpdateToolWindowsPaneCmd(commandsList);
1362 else {
1363 info.setType(type);
1364 appendApplyWindowInfoCmd(info, commandsList);
1368 private void appendApplyWindowInfoCmd(final WindowInfoImpl info, final List<FinalizableCommand> commandsList) {
1369 final StripeButton button = getStripeButton(info.getId());
1370 final InternalDecorator decorator = getInternalDecorator(info.getId());
1371 commandsList.add(new ApplyWindowInfoCmd(info, button, decorator, myWindowManager.getCommandProcessor()));
1375 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddDecoratorCmd
1377 private void appendAddDecoratorCmd(final InternalDecorator decorator,
1378 final WindowInfoImpl info,
1379 final boolean dirtyMode,
1380 final List<FinalizableCommand> commandsList) {
1381 final CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1382 final FinalizableCommand command = myToolWindowsPane.createAddDecoratorCmd(decorator, info, dirtyMode, commandProcessor);
1383 commandsList.add(command);
1387 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createRemoveDecoratorCmd
1389 private void appendRemoveDecoratorCmd(final String id, final boolean dirtyMode, final List<FinalizableCommand> commandsList) {
1390 final FinalizableCommand command = myToolWindowsPane.createRemoveDecoratorCmd(id, dirtyMode, myWindowManager.getCommandProcessor());
1391 commandsList.add(command);
1394 private void appendRemoveFloatingDecoratorCmd(final WindowInfoImpl info, final List<FinalizableCommand> commandsList) {
1395 final RemoveFloatingDecoratorCmd command = new RemoveFloatingDecoratorCmd(info);
1396 commandsList.add(command);
1400 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddButtonCmd
1402 private void appendAddButtonCmd(final StripeButton button, final WindowInfoImpl info, final List<FinalizableCommand> commandsList) {
1403 final Comparator comparator = myLayout.comparator(info.getAnchor());
1404 final CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1405 final FinalizableCommand command = myToolWindowsPane.createAddButtonCmd(button, info, comparator, commandProcessor);
1406 commandsList.add(command);
1410 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createAddButtonCmd
1412 private void appendRemoveButtonCmd(final String id, final List<FinalizableCommand> commandsList) {
1413 final FinalizableCommand command = myToolWindowsPane.createRemoveButtonCmd(id, myWindowManager.getCommandProcessor());
1414 commandsList.add(command);
1417 private ActionCallback appendRequestFocusInEditorComponentCmd(final ArrayList<FinalizableCommand> commandList, boolean forced) {
1418 if (myProject.isDisposed()) return new ActionCallback.Done();
1419 final CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1420 final RequestFocusInEditorComponentCmd command =
1421 new RequestFocusInEditorComponentCmd(FileEditorManagerEx.getInstanceEx(myProject), getFocusManager(), commandProcessor, forced);
1422 commandList.add(command);
1423 return command.getDoneCallback();
1426 private void appendRequestFocusInToolWindowCmd(final String id, final ArrayList<FinalizableCommand> commandList, boolean forced) {
1427 final ToolWindowImpl toolWindow = (ToolWindowImpl)getToolWindow(id);
1428 final FocusWatcher focusWatcher = myId2FocusWatcher.get(id);
1429 commandList.add(new RequestFocusInToolWindowCmd(getFocusManager(), toolWindow, focusWatcher, myWindowManager.getCommandProcessor(), forced));
1433 * @see com.intellij.openapi.wm.impl.ToolWindowsPane#createSetEditorComponentCmd
1435 private void appendSetEditorComponentCmd(final JComponent component, final List<FinalizableCommand> commandsList) {
1436 final CommandProcessor commandProcessor = myWindowManager.getCommandProcessor();
1437 final FinalizableCommand command = myToolWindowsPane.createSetEditorComponentCmd(component, commandProcessor);
1438 commandsList.add(command);
1441 private void appendUpdateToolWindowsPaneCmd(final List<FinalizableCommand> commandsList) {
1442 final JRootPane rootPane = myFrame.getRootPane();
1443 final FinalizableCommand command = new UpdateRootPaneCmd(rootPane, myWindowManager.getCommandProcessor());
1444 commandsList.add(command);
1448 * @return <code>true</code> if tool window with the specified <code>id</code>
1449 * is floating and has modal showing child dialog. Such windows should not be closed
1450 * when auto-hide windows are gone.
1452 private boolean hasModalChild(final WindowInfoImpl info) {
1453 if (!info.isVisible() || !info.isFloating()) {
1454 return false;
1456 final FloatingDecorator decorator = getFloatingDecorator(info.getId());
1457 LOG.assertTrue(decorator != null);
1458 return isModalOrHasModalChild(decorator);
1461 private static boolean isModalOrHasModalChild(final Window window) {
1462 if (window instanceof Dialog) {
1463 final Dialog dialog = (Dialog)window;
1464 if (dialog.isModal() && dialog.isShowing()) {
1465 return true;
1467 final Window[] ownedWindows = dialog.getOwnedWindows();
1468 for (int i = ownedWindows.length - 1; i >= 0; i--) {
1469 if (isModalOrHasModalChild(ownedWindows[i])) {
1470 return true;
1474 return false;
1478 * Helper method. It deactivates all tool windows excepting the tool window
1479 * which should be activated.
1481 private void prepareForActivation(final String id, final List<FinalizableCommand> commandList) {
1482 final WindowInfoImpl toBeActivatedInfo = getInfo(id);
1483 final WindowInfoImpl[] infos = myLayout.getInfos();
1484 for (final WindowInfoImpl info : infos) {
1485 if (id.equals(info.getId())) {
1486 continue;
1488 if (toBeActivatedInfo.isDocked() || toBeActivatedInfo.isSliding()) {
1489 deactivateToolWindowImpl(info.getId(), info.isAutoHide() || info.isSliding(), commandList);
1491 else { // floating window is being activated
1492 deactivateToolWindowImpl(info.getId(), info.isAutoHide() && info.isFloating() && !hasModalChild(info), commandList);
1497 public void clearSideStack() {
1498 mySideStack.clear();
1501 public void readExternal(final Element element) {
1502 for (final Object o : element.getChildren()) {
1503 final Element e = (Element)o;
1504 if (EDITOR_ELEMENT.equals(e.getName())) {
1505 myEditorComponentActive = Boolean.valueOf(e.getAttributeValue(ACTIVE_ATTR_VALUE)).booleanValue();
1507 else if (DesktopLayout.TAG.equals(e.getName())) { // read layout of tool windows
1508 myLayout.readExternal(e);
1509 final WindowInfoImpl[] windowInfos = myLayout.getAllInfos();
1510 for (WindowInfoImpl windowInfo : windowInfos) {
1511 myRestoredToolWindowIds.add(windowInfo.getId());
1517 public void writeExternal(final Element element) {
1518 if (myFrame == null) {
1519 // do nothing if the project was not opened
1520 return;
1522 final String[] ids = getToolWindowIds();
1524 // Update size of all open floating windows. See SCR #18439
1525 for (final String id : ids) {
1526 final WindowInfoImpl info = getInfo(id);
1527 if (info.isVisible()) {
1528 final InternalDecorator decorator = getInternalDecorator(id);
1529 LOG.assertTrue(decorator != null);
1530 decorator.fireResized();
1534 // Save frame's bounds
1535 final Rectangle frameBounds = myFrame.getBounds();
1536 final Element frameElement = new Element(FRAME_ELEMENT);
1537 element.addContent(frameElement);
1538 frameElement.setAttribute(X_ATTR, Integer.toString(frameBounds.x));
1539 frameElement.setAttribute(Y_ATTR, Integer.toString(frameBounds.y));
1540 frameElement.setAttribute(WIDTH_ATTR, Integer.toString(frameBounds.width));
1541 frameElement.setAttribute(HEIGHT_ATTR, Integer.toString(frameBounds.height));
1542 frameElement.setAttribute(EXTENDED_STATE_ATTR, Integer.toString(myFrame.getExtendedState()));
1543 // Save whether editor is active or not
1544 final Element editorElement = new Element(EDITOR_ELEMENT);
1545 editorElement.setAttribute(ACTIVE_ATTR_VALUE, myEditorComponentActive ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
1546 element.addContent(editorElement);
1547 // Save layout of tool windows
1548 final Element layoutElement = new Element(DesktopLayout.TAG);
1549 element.addContent(layoutElement);
1550 myLayout.writeExternal(layoutElement);
1553 public void setDefaultState(@NotNull final ToolWindowImpl toolWindow,
1554 @Nullable final ToolWindowAnchor anchor,
1555 @Nullable final ToolWindowType type,
1556 @Nullable final Rectangle floatingBounds) {
1558 final WindowInfoImpl info = getInfo(toolWindow.getId());
1559 if (info.wasRead()) return;
1561 if (floatingBounds != null) {
1562 info.setFloatingBounds(floatingBounds);
1565 if (anchor != null) {
1566 toolWindow.setAnchor(anchor, null);
1569 if (type != null) {
1570 toolWindow.setType(type, null);
1575 public void setDefaultContentUiType(ToolWindowImpl toolWindow, ToolWindowContentUiType type) {
1576 final WindowInfoImpl info = getInfo(toolWindow.getId());
1577 if (info.wasRead()) return;
1578 toolWindow.setContentUiType(type, null);
1582 public void doWhenFocusSettlesDown(@NotNull final Runnable runnable) {
1583 final boolean needsRestart = isIdleQueueEmpty();
1584 myIdleRequests.add(runnable);
1585 if (needsRestart) {
1586 restartIdleAlarm();
1590 private void restartIdleAlarm() {
1591 myIdleAlarm.cancelAllRequests();
1592 myIdleAlarm.addRequest(myIdleRunnable, Registry.intValue("actionSystem.focusIdleTimeout"));
1595 private void flushIdleRequests() {
1596 try {
1597 myFlushingIdleRequestsEntryCount++;
1599 final KeyEvent[] events = myToDispatchOnDone.toArray(new KeyEvent[myToDispatchOnDone.size()]);
1600 IdeEventQueue.getInstance().getKeyEventDispatcher().resetState();
1602 boolean keyWasPressed = false;
1604 for (KeyEvent each : events) {
1605 if (!isFocusTransferReady()) break;
1607 if (!keyWasPressed) {
1608 if (each.getID() == KeyEvent.KEY_PRESSED) {
1609 keyWasPressed = true;
1611 else {
1612 myToDispatchOnDone.remove(each);
1613 continue;
1617 final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
1618 if (owner != null && SwingUtilities.getWindowAncestor(owner) != null) {
1619 myToDispatchOnDone.remove(each);
1620 IdeEventQueue.getInstance().dispatchEvent(
1621 new KeyEvent(owner, each.getID(), each.getWhen(), each.getModifiersEx(), each.getKeyCode(), each.getKeyChar(),
1622 each.getKeyLocation()));
1624 else {
1625 break;
1630 if (isPendingKeyEventsRedispatched()) {
1631 final Runnable[] all = myIdleRequests.toArray(new Runnable[myIdleRequests.size()]);
1632 myIdleRequests.clear();
1633 for (Runnable each : all) {
1634 each.run();
1638 finally {
1639 myFlushingIdleRequestsEntryCount--;
1640 if (!isIdleQueueEmpty()) {
1641 restartIdleAlarm();
1646 public boolean isFocusTransferReady() {
1647 if (!myFocusRequests.isEmpty()) return false;
1648 if (myQueue == null) return true;
1650 return !myQueue.isSuspendMode() && !myQueue.hasFocusEventsPending();
1653 private boolean isIdleQueueEmpty() {
1654 return isPendingKeyEventsRedispatched() && myIdleRequests.isEmpty();
1657 private boolean isPendingKeyEventsRedispatched() {
1658 return myToDispatchOnDone.isEmpty();
1661 public boolean dispatch(KeyEvent e) {
1662 if (!Registry.is("actionSystem.fixLostTyping")) return false;
1664 if (myFlushingIdleRequestsEntryCount > 0) return false;
1666 if (!isFocusTransferReady() || !isPendingKeyEventsRedispatched()) {
1667 for (FocusCommand each : myFocusRequests) {
1668 final KeyEventProcessor processor = each.getProcessor();
1669 if (processor != null) {
1670 final Boolean result = processor.dispatch(e, myKeyProcessorContext);
1671 if (result != null) {
1672 return result.booleanValue();
1677 myToDispatchOnDone.add(e);
1678 restartIdleAlarm();
1680 return true;
1682 else {
1683 return false;
1687 public void suspendKeyProcessingUntil(final ActionCallback done) {
1688 requestFocus(new FocusCommand(done) {
1689 public ActionCallback run() {
1690 return done;
1692 }.saveAllocation(), true);
1695 public Expirable getTimestamp(final boolean trackOnlyForcedCommands) {
1696 return new Expirable() {
1697 long myOwnStamp = trackOnlyForcedCommands ? myForcedCmdTimestamp : myCmdTimestamp;
1699 public boolean isExpired() {
1700 return myOwnStamp < (trackOnlyForcedCommands ? myForcedCmdTimestamp : myCmdTimestamp);
1705 public void stretchWidth(ToolWindowImpl toolWindow, int value) {
1706 if (!toolWindow.isVisible()) return;
1708 myToolWindowsPane.stretchWidth(toolWindow, value);
1711 public void stretchHeight(ToolWindowImpl toolWindow, int value) {
1712 if (!toolWindow.isVisible()) return;
1714 myToolWindowsPane.stretchHeight(toolWindow, value);
1719 * This command creates and shows <code>FloatingDecorator</code>.
1721 private final class AddFloatingDecoratorCmd extends FinalizableCommand {
1722 private final FloatingDecorator myFloatingDecorator;
1725 * Creates floating decorator for specified floating decorator.
1727 private AddFloatingDecoratorCmd(final InternalDecorator decorator, final WindowInfoImpl info) {
1728 super(myWindowManager.getCommandProcessor());
1729 myFloatingDecorator = new FloatingDecorator(myFrame, info.copy(), decorator);
1730 myId2FloatingDecorator.put(info.getId(), myFloatingDecorator);
1731 final Rectangle bounds = info.getFloatingBounds();
1732 if (bounds != null &&
1733 bounds.width > 0 &&
1734 bounds.height > 0 &&
1735 myWindowManager.isInsideScreenBounds(bounds.x, bounds.y, bounds.width)) {
1736 myFloatingDecorator.setBounds(bounds);
1738 else { // place new frame at the center of main frame if there are no floating bounds
1739 Dimension size = decorator.getSize();
1740 if (size.width == 0 || size.height == 0) {
1741 size = decorator.getPreferredSize();
1743 myFloatingDecorator.setSize(size);
1744 myFloatingDecorator.setLocationRelativeTo(myFrame);
1748 public void run() {
1749 try {
1750 myFloatingDecorator.show();
1752 finally {
1753 finish();
1759 * This command hides and destroys floating decorator for tool window
1760 * with specified <code>ID</code>.
1762 private final class RemoveFloatingDecoratorCmd extends FinalizableCommand {
1763 private final FloatingDecorator myFloatingDecorator;
1765 private RemoveFloatingDecoratorCmd(final WindowInfoImpl info) {
1766 super(myWindowManager.getCommandProcessor());
1767 myFloatingDecorator = getFloatingDecorator(info.getId());
1768 myId2FloatingDecorator.remove(info.getId());
1769 info.setFloatingBounds(myFloatingDecorator.getBounds());
1772 public void run() {
1773 try {
1774 if (Patches.SPECIAL_WINPUT_METHOD_PROCESSING) {
1775 myFloatingDecorator.remove(myFloatingDecorator.getRootPane());
1777 myFloatingDecorator.dispose();
1779 finally {
1780 finish();
1784 @Nullable
1785 public Condition getExpireCondition() {
1786 return Condition.FALSE;
1790 private final class EditorComponentFocusWatcher extends FocusWatcher {
1791 protected void focusedComponentChanged(final Component component, final AWTEvent cause) {
1792 if (myWindowManager.getCommandProcessor().getCommandCount() > 0 || component == null) {
1793 return;
1796 // Sometimes focus gained comes when editor is active. For example it can happen when
1797 // user switches between menus or closes some dialog. In that case we just ignore this event,
1798 // i.e. don't initiate deactivation of tool windows and requesting focus in editor.
1799 if (myEditorComponentActive) {
1800 return;
1804 final KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
1805 final Component owner = mgr.getFocusOwner();
1807 IdeFocusManager.getInstance(myProject).doWhenFocusSettlesDown(new Runnable() {
1808 public void run() {
1809 if (mgr.getFocusOwner() == owner) {
1810 activateEditorComponent(false);
1819 * Notifies window manager about focus traversal in tool window
1821 private final class ToolWindowFocusWatcher extends FocusWatcher {
1822 private final String myId;
1825 private ToolWindowFocusWatcher(final ToolWindowImpl toolWindow) {
1826 myId = toolWindow.getId();
1827 install(toolWindow.getComponent());
1830 protected boolean isFocusedComponentChangeValid(final Component comp, final AWTEvent cause) {
1831 return myWindowManager.getCommandProcessor().getCommandCount() == 0 && comp != null;
1834 protected void focusedComponentChanged(final Component component, final AWTEvent cause) {
1835 if (myWindowManager.getCommandProcessor().getCommandCount() > 0 || component == null) {
1836 return;
1838 final WindowInfoImpl info = getInfo(myId);
1839 myFocusedComponentAlaram.cancelAllRequests();
1841 if (!info.isActive()) {
1842 myFocusedComponentAlaram.addRequest(new EdtRunnable() {
1843 public void runEdt() {
1844 if (!myLayout.isToolWindowRegistered(myId)) return;
1845 activateToolWindow(myId, false, false);
1847 }, 100);
1853 * Spies on IdeToolWindow properties and applies them to the window
1854 * state.
1856 private final class MyToolWindowPropertyChangeListener implements PropertyChangeListener {
1857 public void propertyChange(final PropertyChangeEvent e) {
1858 final ToolWindowImpl toolWindow = (ToolWindowImpl)e.getSource();
1859 if (ToolWindowEx.PROP_AVAILABLE.equals(e.getPropertyName())) {
1860 final WindowInfoImpl info = getInfo(toolWindow.getId());
1861 if (!toolWindow.isAvailable() && info.isVisible()) {
1862 hideToolWindow(toolWindow.getId(), false);
1869 * Translates events from InternalDecorator into ToolWindowManager method invocations.
1871 private final class MyInternalDecoratorListener implements InternalDecoratorListener {
1872 public void anchorChanged(final InternalDecorator source, final ToolWindowAnchor anchor) {
1873 setToolWindowAnchor(source.getToolWindow().getId(), anchor);
1876 public void autoHideChanged(final InternalDecorator source, final boolean autoHide) {
1877 setToolWindowAutoHide(source.getToolWindow().getId(), autoHide);
1880 public void hidden(final InternalDecorator source) {
1881 hideToolWindow(source.getToolWindow().getId(), false);
1884 public void hiddenSide(final InternalDecorator source) {
1885 hideToolWindow(source.getToolWindow().getId(), true);
1888 public void contentUiTypeChanges(InternalDecorator source, ToolWindowContentUiType type) {
1889 setContentUiType(source.getToolWindow().getId(), type);
1893 * Handles event from decorator and modify weight/floating bounds of the
1894 * tool window depending on decoration type.
1896 public void resized(final InternalDecorator source) {
1897 final WindowInfoImpl info = getInfo(source.getToolWindow().getId());
1898 if (info.isFloating()) {
1899 final Window owner = SwingUtilities.getWindowAncestor(source);
1900 if (owner != null) {
1901 info.setFloatingBounds(owner.getBounds());
1904 else { // docked and sliding windows
1905 if (ToolWindowAnchor.TOP == info.getAnchor() || ToolWindowAnchor.BOTTOM == info.getAnchor()) {
1906 info.setWeight((float)source.getHeight() / (float)myToolWindowsPane.getMyLayeredPane().getHeight());
1907 float newSideWeight = (float)source.getWidth() / (float)myToolWindowsPane.getMyLayeredPane().getWidth();
1908 if (newSideWeight < 1.0f) {
1909 info.setSideWeight(newSideWeight);
1912 else {
1913 info.setWeight((float)source.getWidth() / (float)myToolWindowsPane.getMyLayeredPane().getWidth());
1914 float newSideWeight = (float)source.getHeight() / (float)myToolWindowsPane.getMyLayeredPane().getHeight();
1915 if (newSideWeight < 1.0f) {
1916 info.setSideWeight(newSideWeight);
1922 public void activated(final InternalDecorator source) {
1923 activateToolWindow(source.getToolWindow().getId(), true, true);
1926 public void typeChanged(final InternalDecorator source, final ToolWindowType type) {
1927 setToolWindowType(source.getToolWindow().getId(), type);
1930 public void sideStatusChanged(final InternalDecorator source, final boolean isSideTool) {
1931 setSideTool(source.getToolWindow().getId(), isSideTool);
1935 private void updateComponentTreeUI() {
1936 ApplicationManager.getApplication().assertIsDispatchThread();
1937 final WindowInfoImpl[] infos = myLayout.getInfos();
1938 for (final WindowInfoImpl info : infos) {
1939 if (info.isVisible()) { // skip visible tool windows (optimization)
1940 continue;
1942 SwingUtilities.updateComponentTreeUI(getInternalDecorator(info.getId()));
1946 private final class MyUIManagerPropertyChangeListener implements PropertyChangeListener {
1947 public void propertyChange(final PropertyChangeEvent e) {
1948 updateComponentTreeUI();
1952 private final class MyLafManagerListener implements LafManagerListener {
1953 public void lookAndFeelChanged(final LafManager source) {
1954 updateComponentTreeUI();
1959 public WindowManagerEx getWindowManager() {
1960 return myWindowManager;
1963 @NotNull
1964 public String getComponentName() {
1965 return "ToolWindowManager";
1969 public ToolWindowsPane getToolWindowsPane() {
1970 return myToolWindowsPane;
1973 public ActionCallback requestFocus(final Component c, final boolean forced) {
1974 return requestFocus(new FocusCommand.ByComponent(c), forced);
1977 public ActionCallback requestDefaultFocus(final boolean forced) {
1978 return requestFocus(new FocusCommand() {
1979 public ActionCallback run() {
1980 return processDefaultFocusRequest(forced);
1982 }, forced);
1985 private ActionCallback processDefaultFocusRequest(boolean forced) {
1986 if (ModalityState.NON_MODAL.equals(ModalityState.current())) {
1987 final String activeId = getActiveToolWindowId();
1988 if (myEditorComponentActive || activeId == null || getToolWindow(activeId) == null) {
1989 activateEditorComponent(forced);
1990 } else {
1991 activateToolWindow(activeId, forced, false);
1994 return new ActionCallback.Done();
1997 public ActionCallback requestFocus(final FocusCommand command, final boolean forced) {
1998 final ActionCallback result = new ActionCallback();
2000 if (!forced) {
2001 myFocusRequests.add(command);
2003 SwingUtilities.invokeLater(new Runnable() {
2004 public void run() {
2005 resetUnforcedCommand(command);
2006 _requestFocus(command, forced, result);
2010 else {
2011 _requestFocus(command, forced, result);
2014 result.doWhenProcessed(new Runnable() {
2015 public void run() {
2016 restartIdleAlarm();
2020 return result;
2023 private void _requestFocus(final FocusCommand command, final boolean forced, final ActionCallback result) {
2024 if (checkForRejectOrByPass(command, forced, result)) return;
2026 setCommand(command);
2028 if (forced) {
2029 myForcedFocusRequestsAlarm.cancelAllRequests();
2030 setLastEffectiveForcedRequest(command);
2033 SwingUtilities.invokeLater(new Runnable() {
2034 public void run() {
2035 if (checkForRejectOrByPass(command, forced, result)) return;
2037 if (myRequestFocusCmd == command) {
2038 final ActionCallback.TimedOut focusTimeout =
2039 new ActionCallback.TimedOut(Registry.intValue("actionSystem.commandProcessingTimeout"),
2040 "Focus command timed out, cmd=" + command, command.getAllocation(), true) {
2041 @Override
2042 protected void onTimeout() {
2043 forceFinishFocusSettledown(command, result);
2047 myCmdTimestamp++;
2048 if (forced) {
2049 myForcedCmdTimestamp++;
2052 command.run().doWhenDone(new Runnable() {
2053 public void run() {
2054 SwingUtilities.invokeLater(new Runnable() {
2055 public void run() {
2056 result.setDone();
2060 }).doWhenRejected(new Runnable() {
2061 public void run() {
2062 result.setRejected();
2064 }).doWhenProcessed(new Runnable() {
2065 public void run() {
2066 resetCommand(command);
2068 if (forced) {
2069 myForcedFocusRequestsAlarm.addRequest(new EdtRunnable() {
2070 public void runEdt() {
2071 setLastEffectiveForcedRequest(null);
2073 }, 250);
2076 }).notify(focusTimeout);
2078 else {
2079 rejectCommand(command, result);
2085 private void forceFinishFocusSettledown(FocusCommand cmd, ActionCallback cmdCallback) {
2086 rejectCommand(cmd, cmdCallback);
2089 private boolean checkForRejectOrByPass(final FocusCommand cmd, final boolean forced, final ActionCallback result) {
2090 if (cmd.isExpired()) {
2091 rejectCommand(cmd, result);
2092 return true;
2095 final FocusCommand lastRequest = getLastEffectiveForcedRequest();
2097 if (!forced && !isUnforcedRequestAllowed()) {
2098 if (cmd.equals(lastRequest)) {
2099 result.setDone();
2101 else {
2102 rejectCommand(cmd, result);
2104 return true;
2108 if (lastRequest != null && lastRequest.dominatesOver(cmd)) {
2109 rejectCommand(cmd, result);
2110 return true;
2113 boolean doNotExecuteBecauseAppIsInactive = !myApp.isActive()
2114 && (!canExecuteOnInactiveApplication(cmd) && Registry.is("actionSystem.suspendFocusTransferIfApplicationInactive"));
2115 if (doNotExecuteBecauseAppIsInactive) {
2116 if (myCallbackOnActivation != null) {
2117 myCallbackOnActivation.setRejected();
2120 myFocusCommandOnAppActivation = cmd;
2121 myCallbackOnActivation = result;
2123 return true;
2126 return false;
2129 private void rejectCommand(FocusCommand cmd, ActionCallback callback) {
2130 resetCommand(cmd);
2131 resetUnforcedCommand(cmd);
2133 callback.setRejected();
2136 private void setCommand(FocusCommand command) {
2137 myRequestFocusCmd = command;
2139 if (!myFocusRequests.contains(command)) {
2140 myFocusRequests.add(command);
2144 private void resetCommand(FocusCommand cmd) {
2145 if (cmd == myRequestFocusCmd) {
2146 myRequestFocusCmd = null;
2149 final KeyEventProcessor processor = cmd.getProcessor();
2150 if (processor != null) {
2151 processor.finish(myKeyProcessorContext);
2154 myFocusRequests.remove(cmd);
2157 private void resetUnforcedCommand(FocusCommand cmd) {
2158 myFocusRequests.remove(cmd);
2161 private static boolean canExecuteOnInactiveApplication(FocusCommand cmd) {
2162 return !Patches.REQUEST_FOCUS_MAY_ACTIVATE_APP || cmd.canExecuteOnInactiveApp();
2165 private void setLastEffectiveForcedRequest(FocusCommand command) {
2166 myLastForcedRequest = new WeakReference<FocusCommand>(command);
2169 @Nullable
2170 private FocusCommand getLastEffectiveForcedRequest() {
2171 if (myLastForcedRequest == null) return null;
2172 final FocusCommand request = myLastForcedRequest.get();
2173 return request != null && !request.isExpired() ? request : null;
2176 private boolean isUnforcedRequestAllowed() {
2177 return getLastEffectiveForcedRequest() == null;
2180 private boolean isProjectComponent(Component c) {
2181 final Component frame = UIUtil.findUltimateParent(c);
2182 if (frame instanceof IdeFrame) {
2183 return frame == myWindowManager.getFrame(myProject);
2185 else {
2186 return false;
2190 private class AppListener extends ApplicationAdapter {
2192 @Override
2193 public void applicationDeactivated(IdeFrame ideFrame) {
2194 Component c;
2195 final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
2196 if (isProjectComponent(owner)) {
2197 c = owner;
2198 } else {
2199 c = getLastFocusedProjectComponent();
2202 myFocusedComponentOnDeactivation = c != null ? new WeakReference<Component>(c) : null;
2205 @Override
2206 public void applicationActivated(final IdeFrame ideFrame) {
2207 final FocusCommand cmd = myFocusCommandOnAppActivation;
2208 ActionCallback callback = myCallbackOnActivation;
2209 myFocusCommandOnAppActivation = null;
2210 myCallbackOnActivation = null;
2212 if (cmd != null) {
2213 requestFocus(cmd, true).notify(callback).doWhenRejected(new Runnable() {
2214 public void run() {
2215 focusLastFocusedComponent(ideFrame);
2218 } else {
2219 focusLastFocusedComponent(ideFrame);
2223 private void focusLastFocusedComponent(IdeFrame ideFrame) {
2224 final KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
2225 if (ideFrame == myWindowManager.getFrame(myProject)) {
2226 final Component owner = mgr.getFocusOwner();
2227 Component old = myFocusedComponentOnDeactivation != null ? myFocusedComponentOnDeactivation.get() : null;
2228 if (owner == null && old != null && old.isShowing()) {
2229 requestFocus(old, false);
2231 myFocusedComponentOnDeactivation = null;
2236 private static class EdtAlarm {
2237 private final Alarm myAlarm;
2239 private EdtAlarm(Disposable parent) {
2240 myAlarm = new Alarm(Alarm.ThreadToUse.OWN_THREAD, parent);
2243 public void cancelAllRequests() {
2244 myAlarm.cancelAllRequests();
2247 public void addRequest(EdtRunnable runnable, int delay) {
2248 myAlarm.addRequest(runnable, delay);
2252 private class KeyProcessorConext implements KeyEventProcessor.Context {
2253 public List<KeyEvent> getQueue() {
2254 return myToDispatchOnDone;
2257 public void dispatch(final List<KeyEvent> events) {
2258 doWhenFocusSettlesDown(new Runnable() {
2259 public void run() {
2260 myToDispatchOnDone.addAll(events);
2261 restartIdleAlarm();