replaced <code></code> with more concise {@code}
[idea/community.git] / platform / platform-api / src / com / intellij / openapi / wm / IdeFocusManager.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.wm;
17
18 import com.intellij.openapi.actionSystem.CommonDataKeys;
19 import com.intellij.openapi.actionSystem.DataContext;
20 import com.intellij.openapi.application.Application;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.application.ModalityState;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.util.ActionCallback;
25 import com.intellij.openapi.util.Expirable;
26 import com.intellij.openapi.util.ExpirableRunnable;
27 import com.intellij.util.ui.UIUtil;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
30
31 import javax.swing.*;
32 import java.awt.*;
33 import java.awt.event.KeyEvent;
34
35 /**
36  * This class receives focus requests, manages the, and delegates to the awt focus subsystem. All focus requests
37  * should be done through this class. For example, to request focus on a component:
38  * <pre>
39  *   IdeFocusManager.getInstance(project).requestFocus(comp, true);
40  * </pre>
41  * This is the preferred way to request focus on components to
42  * <pre>
43  *   comp.requestFocus();
44  * </pre>
45  *
46  * This class is also responsible for delivering key events while focus transferring is in progress.
47  * <p>
48  * {@code IdeFocusManager} instance can be received per project or the global instance. The preferred way is
49  * to use instance {@code IdeFocusManager.getInstance(project)}. If no project instance is available, then
50  * {@code IdeFocusManager.getGlobalInstance()} can be used.
51  */
52
53 public abstract class IdeFocusManager implements FocusRequestor {
54
55   public ActionCallback requestFocusInProject(@NotNull Component c, @Nullable Project project) {
56     return requestFocus(c, false);
57   }
58
59   /**
60    * Finds most suitable component to request focus to. For instance you may pass a JPanel instance,
61    * this method will traverse into it's children to find focusable component
62    * @return suitable component to focus
63    */
64   @Nullable
65   public abstract JComponent getFocusTargetFor(@NotNull final JComponent comp);
66
67
68   /**
69    * Executes given runnable after all focus activities are finished
70    */
71   public abstract void doWhenFocusSettlesDown(@NotNull Runnable runnable);
72
73   /**
74    * Executes given runnable after all focus activities are finished, immediately or later with the given modaliy state
75    */
76   public abstract void doWhenFocusSettlesDown(@NotNull Runnable runnable, @NotNull ModalityState modality);
77
78   /**
79    * Executes given runnable after all focus activities are finished
80    */
81   public abstract void doWhenFocusSettlesDown(@NotNull ExpirableRunnable runnable);
82
83
84   /**
85    * Finds focused component among descendants of the given component. Descendants may be in child popups and windows
86    */
87   @Nullable
88   public abstract Component getFocusedDescendantFor(final Component comp);
89
90   /**
91    * Dispatches given key event. This methods should not be called by the user code
92    * @return true is the event was dispatched, false - otherwise.
93    */
94   public abstract boolean dispatch(@NotNull KeyEvent e);
95
96   @Deprecated
97   // use #typeAheadUntil(ActionCallback, String) instead
98   public void typeAheadUntil(ActionCallback done) {
99     typeAheadUntil(done, "No cause has been provided");
100   }
101
102   /**
103    * Aggregates all key events until given callback object is processed
104    * @param done action callback
105    */
106   public void typeAheadUntil(ActionCallback done, @NotNull String cause) {}
107
108   /**
109    * Reports if any focus activity is being done
110    */
111   public abstract boolean isFocusBeingTransferred();
112
113   /**
114    * Requests default focus. The method should not be called by the user code.
115    */
116   @NotNull
117   public abstract ActionCallback requestDefaultFocus(boolean forced);
118
119   /**
120    * Reports of focus transfer is enabled right now. It can be disabled if app is inactive. In this case
121    * all focus requests will be either postponed or executed only if {@code FocusCommand} can be executed on an inaactive app.
122    * @see com.intellij.openapi.wm.FocusCommand#canExecuteOnInactiveApp()
123    */
124   public abstract boolean isFocusTransferEnabled();
125
126   /**
127    * Returns {@code Expirable} instance for the given counter of focus commands. As any new {@code FocusCommand}
128    * is emitted to execute, the counter increments thus making the returned {@code Expirable} objects expired.
129    */
130   @NotNull
131   public abstract Expirable getTimestamp(boolean trackOnlyForcedCommands);
132
133   /**
134    * Returns {@code FocusRequestor} object which will emit focus requests unless expired.
135    * @see #getTimestamp(boolean)
136    */
137   @NotNull
138   public abstract FocusRequestor getFurtherRequestor();
139
140   /**
141    * Injects some procedure that will maybe do something with focus after all focus requests are fulfilled and
142    * before focus transfer is reported ready.
143    */
144   public abstract void revalidateFocus(@NotNull ExpirableRunnable runnable);
145
146   /**
147    * Enables or disables typeahead
148    * @see #typeAheadUntil(com.intellij.openapi.util.ActionCallback)
149    */
150   public abstract void setTypeaheadEnabled(boolean enabled);
151
152   /**
153    * Computes effective focus owner
154    */
155   public abstract Component getFocusOwner();
156
157   /**
158    * Runs runnable for which {@code DataContext} will no be computed from the current focus owner,
159    * but used the given one
160    */
161   public abstract void runOnOwnContext(@NotNull DataContext context, @NotNull Runnable runnable);
162
163   /**
164    * Returns last focused component for the given {@code IdeFrame}
165    */
166   @Nullable
167   public abstract Component getLastFocusedFor(@Nullable IdeFrame frame);
168
169   /**
170    * Returns last focused {@code IdeFrame}
171    */
172   @Nullable
173   public abstract IdeFrame getLastFocusedFrame();
174
175   /**
176    * Put the container window to front. May not execute of the app is inactive or under some other conditions. This
177    * is the preferred way to finding the container window and unconditionally calling {@code window.toFront()}
178    */
179   public abstract void toFront(JComponent c);
180
181   public static IdeFocusManager getInstance(@Nullable Project project) {
182     if (project == null || project.isDisposed() || !project.isInitialized()) return getGlobalInstance();
183
184     return project.getComponent(IdeFocusManager.class);
185   }
186
187   @NotNull
188   public static IdeFocusManager findInstanceByContext(@Nullable DataContext context) {
189     IdeFocusManager instance = null;
190     if (context != null) {
191       instance = getInstanceSafe(CommonDataKeys.PROJECT.getData(context));
192     }
193
194     if (instance == null) {
195       instance = findByComponent(KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow());
196     }
197
198     if (instance == null) {
199       instance = getGlobalInstance();
200     }
201
202     return instance;
203   }
204
205   @NotNull
206   public static IdeFocusManager findInstanceByComponent(@NotNull Component c) {
207     final IdeFocusManager instance = findByComponent(c);
208     return instance != null ? instance : findInstanceByContext(null);
209   }
210
211
212   @Nullable
213   private static IdeFocusManager findByComponent(Component c) {
214     final Component parent = UIUtil.findUltimateParent(c);
215     if (parent instanceof IdeFrame) {
216       return getInstanceSafe(((IdeFrame)parent).getProject());
217     }
218     return null;
219   }
220
221
222   @Nullable
223   private static IdeFocusManager getInstanceSafe(@Nullable Project project) {
224     if (project != null && !project.isDisposed() && project.isInitialized()) {
225       return getInstance(project);
226     }
227     return null;
228   }
229
230   @NotNull
231   public static IdeFocusManager findInstance() {
232     final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
233     return owner != null ? findInstanceByComponent(owner) : findInstanceByContext(null);
234   }
235
236   @NotNull
237   public static IdeFocusManager getGlobalInstance() {
238     IdeFocusManager fm = null;
239
240     Application app = ApplicationManager.getApplication();
241     if (app != null && app.hasComponent(IdeFocusManager.class)) {
242       fm = app.getComponent(IdeFocusManager.class);
243     }
244
245     if (fm == null) {
246       // happens when app is semi-initialized (e.g. when IDEA server dialog is shown)
247       fm = PassThroughIdeFocusManager.getInstance();
248     }
249
250     return fm;
251   }
252
253 }