blob: c8f5421331499c12264981b14a351c164c6ed041 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.wm.impl.commands;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Expirable;
import com.intellij.openapi.wm.FocusCommand;
import com.intellij.openapi.wm.FocusWatcher;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
import com.intellij.openapi.wm.impl.FloatingDecorator;
import com.intellij.openapi.wm.impl.ToolWindowImpl;
import com.intellij.openapi.wm.impl.WindowManagerImpl;
import com.intellij.openapi.wm.impl.WindowWatcher;
import com.intellij.util.Alarm;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
/**
* Requests focus for the specified tool window.
*
* @author Vladimir Kondratyev
*/
public final class RequestFocusInToolWindowCmd extends FinalizableCommand {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.commands.RequestFocusInToolWindowCmd");
private final ToolWindowImpl myToolWindow;
private final FocusWatcher myFocusWatcher;
private final boolean myForced;
private final Expirable myTimestamp;
public RequestFocusInToolWindowCmd(IdeFocusManager focusManager, final ToolWindowImpl toolWindow, final FocusWatcher focusWatcher, final Runnable finishCallBack, boolean forced) {
super(finishCallBack);
myToolWindow = toolWindow;
myFocusWatcher = focusWatcher;
myForced = forced;
myTimestamp = focusManager.getTimestamp(true);
}
@Override
public final void run() {
myToolWindow.getActivation().doWhenDone(new Runnable() {
@Override
public void run() {
processRequestFocus();
}
});
}
private void processRequestFocus() {
try {
if (myTimestamp.isExpired()) {
return;
}
Component preferredFocusedComponent = myToolWindow.isUseLastFocusedOnActivation() ? myFocusWatcher.getFocusedComponent() : null;
if (preferredFocusedComponent == null && myToolWindow.getContentManager().getSelectedContent() != null) {
preferredFocusedComponent = myToolWindow.getContentManager().getSelectedContent().getPreferredFocusableComponent();
if (preferredFocusedComponent != null) {
preferredFocusedComponent = IdeFocusTraversalPolicy.getPreferredFocusedComponent((JComponent)preferredFocusedComponent);
}
}
if (preferredFocusedComponent == null) {
preferredFocusedComponent = myFocusWatcher.getNearestFocusableComponent();
if (preferredFocusedComponent instanceof JComponent) {
preferredFocusedComponent = IdeFocusTraversalPolicy.getPreferredFocusedComponent((JComponent)preferredFocusedComponent);
}
}
if (preferredFocusedComponent != null) {
// When we get remembered component this component can be already invisible
if (!preferredFocusedComponent.isShowing()) {
preferredFocusedComponent = null;
}
}
if (preferredFocusedComponent == null) {
final JComponent component = myToolWindow.getComponent();
preferredFocusedComponent = IdeFocusTraversalPolicy.getPreferredFocusedComponent(component);
}
// Try to focus component which is preferred one for the tool window
if (preferredFocusedComponent != null) {
requestFocus(preferredFocusedComponent).doWhenDone(new Runnable() {
@Override
public void run() {
bringOwnerToFront();
}
});
}
else {
// If there is no preferred component then try to focus tool window itself
final JComponent componentToFocus = myToolWindow.getComponent();
requestFocus(componentToFocus).doWhenDone(new Runnable() {
@Override
public void run() {
bringOwnerToFront();
}
});
}
}
finally {
finish();
}
}
private void bringOwnerToFront() {
final Window owner = SwingUtilities.getWindowAncestor(myToolWindow.getComponent());
//Toolwindow component shouldn't take focus back if new dialog or frame appears
//Example: Ctrl+D on file history brings a diff dialog to front and then hides it by main frame by calling
// toFront on toolwindow window
Window activeFrame = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
if (activeFrame != null && activeFrame != owner) return;
//if (owner == null) {
// System.out.println("owner = " + owner);
// return;
//}
// if owner is active window or it has active child window which isn't floating decorator then
// don't bring owner window to font. If we will make toFront every time then it's possible
// the following situation:
// 1. user perform refactoring
// 2. "Do not show preview" dialog is popping up.
// 3. At that time "preview" tool window is being activated and modal "don't show..." dialog
// isn't active.
if (owner != null && owner.getFocusOwner() == null) {
final Window activeWindow = getActiveWindow(owner.getOwnedWindows());
if (activeWindow == null || activeWindow instanceof FloatingDecorator) {
LOG.debug("owner.toFront()");
//Thread.dumpStack();
//System.out.println("------------------------------------------------------");
owner.toFront();
}
}
}
@NotNull
private ActionCallback requestFocus(@NotNull final Component c) {
final ActionCallback result = new ActionCallback();
final Alarm checkerAlarm = new Alarm(result);
Runnable checker = new Runnable() {
final long startTime = System.currentTimeMillis();
@Override
public void run() {
if (System.currentTimeMillis() - startTime > 10000) {
result.setRejected();
return;
}
if (c.isShowing()) {
final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
if (owner != null && owner == c) {
myManager.getFocusManager().requestFocus(new FocusCommand() {
@Override
@NotNull
public ActionCallback run() {
return new ActionCallback.Done();
}
}, myForced).doWhenProcessed(new Runnable() {
@Override
public void run() {
updateToolWindow(c);
}
}).notify(result);
}
else {
myManager.getFocusManager().requestFocus(new FocusCommand.ByComponent(c, myToolWindow.getComponent(), new Exception()), myForced)
.doWhenProcessed(new Runnable() {
@Override
public void run() {
updateToolWindow(c);
}
}).notify(result);
}
}
else {
checkerAlarm.addRequest(this, 100);
}
}
};
checkerAlarm.addRequest(checker, 0);
return result;
}
private void updateToolWindow(Component c) {
if (c.isFocusOwner()) {
myFocusWatcher.setFocusedComponentImpl(c);
if (myToolWindow.isAvailable() && !myToolWindow.isActive()) {
myToolWindow.activate(null, true, false);
}
}
updateFocusedComponentForWatcher(c);
}
private static void updateFocusedComponentForWatcher(final Component c) {
final WindowWatcher watcher = ((WindowManagerImpl)WindowManager.getInstance()).getWindowWatcher();
final FocusWatcher focusWatcher = watcher.getFocusWatcherFor(c);
if (focusWatcher != null && c.isFocusOwner()) {
focusWatcher.setFocusedComponentImpl(c);
}
}
/**
* @return first active window from hierarchy with specified roots. Returns <code>null</code>
* if there is no active window in the hierarchy.
*/
private static Window getActiveWindow(final Window[] windows) {
for (Window window : windows) {
if (window.isShowing() && window.isActive()) {
return window;
}
window = getActiveWindow(window.getOwnedWindows());
if (window != null) {
return window;
}
}
return null;
}
}