changedUpdate exception
[fedora-idea.git] / platform / platform-impl / src / com / intellij / ui / FocusTrackback.java
blobb377857f663857803ca00ad9fe7d6fec67d3f4b4
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.ui;
18 import com.intellij.ide.DataManager;
19 import com.intellij.openapi.actionSystem.DataContext;
20 import com.intellij.openapi.actionSystem.PlatformDataKeys;
21 import com.intellij.openapi.application.Application;
22 import com.intellij.openapi.application.ApplicationManager;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.ui.popup.JBPopup;
26 import com.intellij.openapi.util.ActionCallback;
27 import com.intellij.openapi.wm.FocusCommand;
28 import com.intellij.openapi.wm.IdeFocusManager;
29 import com.intellij.util.ui.UIUtil;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
33 import javax.swing.*;
34 import java.awt.*;
35 import java.lang.ref.WeakReference;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.WeakHashMap;
41 public class FocusTrackback {
43 private static final Logger LOG = Logger.getInstance("#com.intellij.ui.FocusTrackback");
45 private Window myParentWindow;
47 private Window myRoot;
49 private Component myFocusOwner;
50 private Component myLocalFocusOwner;
52 private static final Map<Window, List<FocusTrackback>> ourRootWindowToParentsStack = new WeakHashMap<Window, List<FocusTrackback>>();
53 private static final Map<Window, WeakReference<Component>> ourRootWindowToFocusedMap =
54 new WeakHashMap<Window, WeakReference<Component>>();
56 private String myRequestorName;
57 private ComponentQuery myFocusedComponentQuery;
58 private boolean myMustBeShown;
60 private boolean myConsumed;
61 private WeakReference myRequestor;
62 private boolean mySheduledForRestore;
63 private boolean myWillBeSheduledForRestore;
65 public FocusTrackback(@NotNull Object requestor, Component parent, boolean mustBeShown) {
66 this(requestor, SwingUtilities.getWindowAncestor(parent), mustBeShown);
69 public FocusTrackback(@NotNull Object requestor, Window parent, boolean mustBeShown) {
70 myRequestor = new WeakReference<Object>(requestor);
71 myRequestorName = requestor.toString();
72 myParentWindow = parent;
73 myMustBeShown = mustBeShown;
76 final Application app = ApplicationManager.getApplication();
77 if (app == null || app.isUnitTestMode() || wrongOS()) return;
79 register(parent);
81 final List<FocusTrackback> stack = getStackForRoot(myRoot);
82 final int index = stack.indexOf(this);
84 //todo [kirillk] diagnostics for IDEADEV-28766
85 assert index >= 0 : myRequestorName;
87 final KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
88 setLocalFocusOwner(manager.getPermanentFocusOwner());
90 if (index == 0) {
91 setFocusOwner(manager.getPermanentFocusOwner());
92 if (getFocusOwner() == null) {
93 final Window window = manager.getActiveWindow();
94 if (window instanceof Provider) {
95 final FocusTrackback other = ((Provider)window).getFocusTrackback();
96 if (other != null) {
97 setFocusOwner(other.getFocusOwner());
102 else {
103 setFocusOwner(stack.get(0).getFocusOwner());
106 if (stack.size() == 1 && getFocusOwner() == null) {
107 setFocusOwner(getFocusFor(myRoot));
109 else if (index == 0 && getFocusOwner() != null) {
110 setFocusFor(myRoot, getFocusOwner());
114 private void setLocalFocusOwner(Component component) {
115 myLocalFocusOwner = component;
118 private static Component getFocusFor(Window parent) {
119 final WeakReference<Component> ref = ourRootWindowToFocusedMap.get(parent);
120 return ref != null ? ref.get() : null;
123 private static void setFocusFor(Window parent, Component focus) {
124 ourRootWindowToFocusedMap.put(parent, new WeakReference<Component>(focus));
127 private static boolean wrongOS() {
128 return false;
131 public void registerFocusComponent(@NotNull final Component focusedComponent) {
132 registerFocusComponent(new ComponentQuery() {
133 public Component getComponent() {
134 return focusedComponent;
139 public void registerFocusComponent(@NotNull ComponentQuery query) {
140 myFocusedComponentQuery = query;
143 private void register(final Window parent) {
144 myRoot = findUtlimateParent(parent);
145 List<FocusTrackback> stack = getCleanStackForRoot();
146 stack.remove(this);
147 stack.add(this);
150 private List<FocusTrackback> getCleanStackForRoot() {
151 return getCleanStackForRoot(myRoot);
154 private static List<FocusTrackback> getCleanStackForRoot(final Window root) {
155 List<FocusTrackback> stack = getStackForRoot(root);
157 final FocusTrackback[] stackArray = stack.toArray(new FocusTrackback[stack.size()]);
158 for (FocusTrackback eachExisting : stackArray) {
159 if (eachExisting != null && eachExisting.isConsumed()) {
160 eachExisting.dispose();
162 else if (eachExisting == null) {
163 stack.remove(eachExisting);
166 return stack;
169 public void restoreFocus() {
170 final Application app = ApplicationManager.getApplication();
171 if (app == null || wrongOS() || myConsumed || isSheduledForRestore()) return;
173 Project project = null;
174 DataContext context =
175 myParentWindow == null ? DataManager.getInstance().getDataContext() : DataManager.getInstance().getDataContext(myParentWindow);
176 if (context != null) {
177 project = PlatformDataKeys.PROJECT.getData(context);
180 mySheduledForRestore = true;
181 final List<FocusTrackback> stack = getCleanStackForRoot();
182 final int index = stack.indexOf(this);
183 for (int i = index - 1; i >=0; i--) {
184 if (stack.get(i).isSheduledForRestore()) {
185 dispose();
186 return;
190 if (project != null && !project.isDisposed()) {
191 final IdeFocusManager focusManager = IdeFocusManager.getInstance(project);
192 focusManager.requestFocus(new MyFocusCommand(), false).doWhenProcessed(new Runnable() {
193 public void run() {
194 dispose();
198 else {
199 // no ide focus manager, so no way -- do just later
200 //noinspection SSBasedInspection
201 SwingUtilities.invokeLater(new Runnable() {
202 public void run() {
203 _restoreFocus();
204 dispose();
210 private void _restoreFocus() {
211 final List<FocusTrackback> stack = getCleanStack();
213 if (!stack.contains(this)) return;
215 Component toFocus = queryToFocus(stack, this, true);
217 if (toFocus != null) {
218 final Component ownerBySwing = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
219 if (ownerBySwing != null) {
220 final Window ownerBySwingWindow = SwingUtilities.getWindowAncestor(ownerBySwing);
221 if (ownerBySwingWindow != null && ownerBySwingWindow == SwingUtilities.getWindowAncestor(toFocus)) {
222 toFocus = ownerBySwing;
226 if (myParentWindow != null) {
227 final Window to = toFocus instanceof Window ? (Window) toFocus : SwingUtilities.getWindowAncestor(toFocus);
228 if (to != null && UIUtil.findUltimateParent(to) == UIUtil.findUltimateParent(myParentWindow)) { // IDEADEV-34537
229 toFocus.requestFocus();
231 } else {
232 toFocus.requestFocus();
236 stack.remove(this);
237 dispose();
240 private static Component queryToFocus(final List<FocusTrackback> stack, final FocusTrackback trackback, boolean mustBeLastInStack) {
241 final int index = stack.indexOf(trackback);
242 Component toFocus = null;
244 if (trackback.myLocalFocusOwner != null) {
245 toFocus = trackback.myLocalFocusOwner;
247 if (!toFocus.isShowing()) {
248 toFocus = null;
252 if (toFocus == null) {
253 if (index > 0) {
254 final ComponentQuery query = stack.get(index - 1).myFocusedComponentQuery;
255 toFocus = query != null ? query.getComponent() : null;
257 else {
258 toFocus = trackback.getFocusOwner();
262 if (mustBeLastInStack) {
263 for (int i = index + 1; i < stack.size(); i++) {
264 if (!stack.get(i).isConsumed()) {
265 toFocus = null;
266 break;
273 return toFocus;
276 private List<FocusTrackback> getCleanStack() {
277 final List<FocusTrackback> stack = getStackForRoot(myRoot);
279 final FocusTrackback[] all = stack.toArray(new FocusTrackback[stack.size()]);
280 for (FocusTrackback each : all) {
281 if (each == null || (each != this && each.isConsumed())) {
282 stack.remove(each);
285 return stack;
288 private static List<FocusTrackback> getStackForRoot(final Window root) {
289 List<FocusTrackback> stack = ourRootWindowToParentsStack.get(root);
290 if (stack == null) {
291 stack = new ArrayList<FocusTrackback>();
292 ourRootWindowToParentsStack.put(root, stack);
294 return stack;
297 @Nullable
298 private static Window findUtlimateParent(final Window parent) {
299 Window root = parent == null ? JOptionPane.getRootFrame() : parent;
300 while (root != null) {
301 final Container next = root.getParent();
302 if (next == null) break;
303 if (next instanceof Window) {
304 root = (Window)next;
306 final Window nextWindow = SwingUtilities.getWindowAncestor(next);
307 if (nextWindow == null) break;
308 root = nextWindow;
311 return root;
314 @Nullable
315 public Component getFocusOwner() {
316 return myFocusOwner;
319 @SuppressWarnings({"HardCodedStringLiteral"})
320 public String toString() {
321 return getClass().getName() + " requestor: " + myRequestorName + " parent=" + myParentWindow;
324 public void dispose() {
325 consume();
326 getStackForRoot(myRoot).remove(this);
327 mySheduledForRestore = false;
328 myParentWindow = null;
329 myRoot = null;
330 myFocusOwner = null;
331 myLocalFocusOwner = null;
334 private boolean isConsumed() {
335 if (myConsumed) return true;
337 if (myMustBeShown) {
338 return !isSheduledForRestore()
339 && myFocusedComponentQuery != null
340 && myFocusedComponentQuery.getComponent() != null
341 && !myFocusedComponentQuery.getComponent().isShowing();
343 else {
344 return myParentWindow == null || !myParentWindow.isShowing();
348 public void consume() {
349 myConsumed = true;
352 private void setFocusOwner(final Component focusOwner) {
353 myFocusOwner = focusOwner;
356 public void setMustBeShown(final boolean mustBeShown) {
357 myMustBeShown = mustBeShown;
360 public boolean isMustBeShown() {
361 return myMustBeShown;
364 public static void release(@NotNull final JFrame frame) {
365 final Window[] all = ourRootWindowToParentsStack.keySet().toArray(new Window[ourRootWindowToParentsStack.size()]);
366 for (Window each : all) {
367 if (each == null) continue;
369 if (each == frame || SwingUtilities.isDescendingFrom(each, frame)) {
370 ourRootWindowToParentsStack.remove(each);
375 public Object getRequestor() {
376 return myRequestor.get();
379 public void setWillBeSheduledForRestore() {
380 myWillBeSheduledForRestore = true;
383 public boolean isSheduledForRestore() {
384 return mySheduledForRestore;
387 public boolean isWillBeSheduledForRestore() {
388 return myWillBeSheduledForRestore;
391 public interface Provider {
392 FocusTrackback getFocusTrackback();
395 public interface ComponentQuery {
396 Component getComponent();
399 @NotNull
400 public static List<JBPopup> getChildPopups(@NotNull final Component component) {
401 List<JBPopup> result = new ArrayList<JBPopup>();
403 final Window window = SwingUtilities.windowForComponent(component);
404 if (window == null) return result;
406 final List<FocusTrackback> stack = getCleanStackForRoot(findUtlimateParent(window));
408 for (FocusTrackback each : stack) {
409 if (each.isChildFor(component) && each.getRequestor() instanceof JBPopup) {
410 result.add((JBPopup)each.getRequestor());
414 return result;
417 private boolean isChildFor(final Component parent) {
418 final Component toFocus = queryToFocus(getCleanStack(), this, false);
419 if (toFocus == null) return false;
420 if (parent == toFocus) return true;
422 return SwingUtilities.isDescendingFrom(toFocus, parent);
426 private class MyFocusCommand extends FocusCommand {
427 public ActionCallback run() {
428 _restoreFocus();
429 return new ActionCallback.Done();
432 @Override
433 public boolean isExpired() {
434 return isConsumed();
437 public String toString() {
438 return "focus trackback";