replaced <code></code> with more concise {@code}
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / commands / RequestFocusInEditorComponentCmd.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.openapi.wm.impl.commands;
18
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.fileEditor.impl.EditorWindow;
21 import com.intellij.openapi.fileEditor.impl.EditorWithProviderComposite;
22 import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
23 import com.intellij.openapi.util.ActionCallback;
24 import com.intellij.openapi.util.Expirable;
25 import com.intellij.openapi.util.SystemInfo;
26 import com.intellij.openapi.util.registry.Registry;
27 import com.intellij.openapi.wm.IdeFocusManager;
28 import com.intellij.openapi.wm.impl.FloatingDecorator;
29 import com.intellij.openapi.wm.impl.IdeFrameImpl;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
32
33 import javax.swing.*;
34 import java.awt.*;
35
36 /**
37  * Requests focus for the editor component.
38  */
39 public final class RequestFocusInEditorComponentCmd extends FinalizableCommand{
40   private JComponent myComponent;
41   private final boolean myForced;
42   private final ActionCallback myDoneCallback;
43
44   private final IdeFocusManager myFocusManager;
45   private final Expirable myTimestamp;
46
47   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.commands.RequestFocusInEditorComponentCmd");
48
49   public RequestFocusInEditorComponentCmd(@NotNull final EditorsSplitters splitters, IdeFocusManager
50                                           focusManager, final Runnable finishCallBack, boolean forced){
51     super(finishCallBack);
52
53     boolean shouldLogFocuses = Registry.is("ide.log.focuses");
54     if (shouldLogFocuses) {
55       LOG.info(new Exception());
56     }
57     myComponent = null;
58     final EditorWindow window = splitters.getCurrentWindow();
59     if (window != null) {
60       final EditorWithProviderComposite editor = window.getSelectedEditor();
61       if (editor != null) {
62         myComponent = editor.getPreferredFocusedComponent();
63       }
64     }
65
66     myForced = forced;
67     myFocusManager = focusManager;
68
69     myDoneCallback = new ActionCallback();
70
71     myTimestamp = myFocusManager.getTimestamp(true);
72   }
73
74   public ActionCallback getDoneCallback() {
75     return myDoneCallback;
76   }
77
78   public final void run(){
79     try{
80       if (myTimestamp.isExpired()) {
81         final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
82         if (owner != null && owner == myComponent) {
83           myDoneCallback.setDone();
84         } else {
85           myDoneCallback.setRejected();
86         }
87       }
88
89
90       final Window owner = myComponent != null ? SwingUtilities.getWindowAncestor(myComponent) : null;
91       if(owner==null){
92         myDoneCallback.setRejected();
93         return;
94       }
95
96       final Window activeFrame = IdeFrameImpl.getActiveFrame();
97       if (activeFrame != null && owner instanceof IdeFrameImpl && activeFrame != owner) {
98         myDoneCallback.setRejected();
99         return;
100       }
101
102       if(myComponent != null){
103         final boolean forced = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() == null;
104         myFocusManager.requestFocus(myComponent, myForced || forced).notifyWhenDone(myDoneCallback).doWhenDone(() -> {
105           if (SystemInfo.isLinux && Registry.is("suppress.focus.stealing")) return;
106           // if owner is active window or it has active child window which isn't floating decorator then
107           // don't bring owner window to font. If we will make toFront every time then it's possible
108           // the following situation:
109           // 1. user perform refactoring
110           // 2. "Do not show preview" dialog is popping up.
111           // 3. At that time "preview" tool window is being activated and modal "don't show..." dialog
112           // isn't active.
113           if(!owner.isActive()){
114             final Window activeWindow=getActiveWindow(owner.getOwnedWindows());
115             if(activeWindow == null || (activeWindow instanceof FloatingDecorator)){
116               //Thread.dumpStack();
117               //System.out.println("------------------------------------------------------");
118               owner.toFront();
119             }
120           }
121         });
122       } else {
123         myDoneCallback.setRejected();
124       }
125
126     }finally{
127       finish();
128     }
129   }
130
131   /**
132    * @return first active window from hierarchy with specified roots. Returns {@code null}
133    * if there is no active window in the hierarchy.
134    */
135   @Nullable
136   private static Window getActiveWindow(final Window[] windows) {
137     for (Window window1 : windows) {
138       Window window = window1;
139       if (window.isShowing() && window.isActive()) {
140         return window;
141       }
142       window = getActiveWindow(window.getOwnedWindows());
143       if (window != null) {
144         return window;
145       }
146     }
147     return null;
148   }
149 }