replaced <code></code> with more concise {@code}
[idea/community.git] / platform / platform-api / src / com / intellij / openapi / wm / FocusCommand.java
1 /*
2  * Copyright 2000-2015 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.application.ApplicationManager;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.openapi.ui.DialogWrapper;
22 import com.intellij.openapi.ui.popup.util.PopupUtil;
23 import com.intellij.openapi.util.ActionCallback;
24 import com.intellij.openapi.util.ActiveRunnable;
25 import com.intellij.openapi.util.Expirable;
26 import com.intellij.openapi.util.registry.Registry;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
29
30 import javax.swing.*;
31 import java.awt.*;
32 import java.awt.event.FocusAdapter;
33 import java.awt.event.FocusEvent;
34 import java.util.Arrays;
35
36 /**
37  * The container class for focus requests for {@code IdeFocusManager}
38  * @see IdeFocusManager
39  */
40 public abstract class FocusCommand extends ActiveRunnable implements Expirable {
41   protected Component myDominationComponent;
42   private Throwable myAllocation;
43   private ActionCallback myCallback;
44   private boolean myInvalidatesPendingFurtherRequestors = true;
45   private Expirable myExpirable;
46
47   private static long lastProcessedCommandTime = 0;
48   protected final long commandCreationTime = System.currentTimeMillis();
49
50   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.FocusCommand");
51
52   public boolean isForced() {
53     return myForced;
54   }
55
56   public void setForced(boolean forced) {
57     myForced = forced;
58   }
59
60   private boolean myForced;
61
62   protected FocusCommand() {
63     saveAllocation();
64   }
65
66   protected FocusCommand(Component dominationComp) {
67     myDominationComponent = dominationComp;
68     saveAllocation();
69   }
70
71   protected FocusCommand(final Object object) {
72     super(object);
73     saveAllocation();
74   }
75
76   protected FocusCommand(final Object object, Component dominationComp) {
77     super(object);
78     myDominationComponent = dominationComp;
79     saveAllocation();
80   }
81
82   protected FocusCommand(final Object[] objects) {
83     super(objects);
84     saveAllocation();
85   }
86
87   protected FocusCommand(final Object[] objects, Component dominationComp) {
88     super(objects);
89     myDominationComponent = dominationComp;
90     saveAllocation();
91   }
92
93   public final ActionCallback getCallback() {
94     return myCallback;
95   }
96
97   public final void setCallback(ActionCallback callback) {
98     myCallback = callback;
99   }
100
101   public boolean isExpired() {
102     return myExpirable != null && myExpirable.isExpired();
103   }
104
105   public boolean canExecuteOnInactiveApp() {
106     return false;
107   }
108
109   @Nullable
110   public KeyEventProcessor getProcessor() {
111     return null;
112   }
113
114   public boolean invalidatesRequestors() {
115     return myInvalidatesPendingFurtherRequestors;
116   }
117
118   public FocusCommand setExpirable(Expirable expirable) {
119     myExpirable = expirable;
120     return this;
121   }
122
123   public FocusCommand setToInvalidateRequestors(boolean invalidatesPendingFurtherRequestors) {
124     myInvalidatesPendingFurtherRequestors = invalidatesPendingFurtherRequestors;
125     return this;
126   }
127
128   @Nullable
129   public final Component getDominationComponent() {
130     return myDominationComponent;
131   }
132
133   public boolean dominatesOver(FocusCommand cmd) {
134     final Component thisComponent = PopupUtil.getOwner(getDominationComponent());
135     final Component thatComponent = PopupUtil.getOwner(cmd.getDominationComponent());
136
137     if (thisComponent != null && thatComponent != null) {
138       return thisComponent != thatComponent && SwingUtilities.isDescendingFrom(thisComponent, thatComponent);
139     }
140
141     return false;
142   }
143
144   public final FocusCommand saveAllocation() {
145     myAllocation = new Exception();
146     return this;
147   }
148
149   public Throwable getAllocation() {
150     return myAllocation;
151   }
152
153   public boolean canFocusChangeFrom(@Nullable Component component) {
154     return true;
155   }
156
157   @Override
158   public String toString() {
159     final Object[] objects = getEqualityObjects();
160     return "FocusCommand objectCount=" + objects.length + " objects=" + Arrays.asList(objects);
161   }
162
163   public static class ByComponent extends FocusCommand {
164     private Component myToFocus;
165     @Nullable private final Project myProject;
166     private Throwable myAllocation;
167
168     public ByComponent(@Nullable Component toFocus, @NotNull Throwable allocation) {
169       this(toFocus, toFocus, null, allocation);
170     }
171
172     public ByComponent(@Nullable Component toFocus, @Nullable Component dominationComponent, @Nullable Project project, @NotNull Throwable allocation) {
173       super(toFocus, dominationComponent);
174       myProject = project;
175       myAllocation = allocation;
176       myToFocus = toFocus;
177     }
178
179     @NotNull
180     public final ActionCallback run() {
181
182       boolean shouldLogFocuses = Registry.is("ide.log.focuses");
183
184       if (shouldLogFocuses) {
185         myToFocus.addFocusListener(new FocusAdapter() {
186           @Override
187           public void focusGained(FocusEvent e) {
188             if (isExpired()) return;
189             super.focusGained(e);
190             LOG.info("Focus gained on " + myToFocus.getClass().getName());
191             myToFocus.removeFocusListener(this);
192           }
193         });
194       }
195
196
197       if (commandCreationTime > lastProcessedCommandTime) {
198         if (!(myToFocus.requestFocusInWindow())) {
199           if (shouldLogFocuses) {
200             LOG.info("We could not request focus in window on " + myToFocus.getClass().getName());
201             LOG.info(myAllocation);
202           }
203           if (ApplicationManager.getApplication().isActive()) {
204             IdeFrame frame = IdeFocusManager.getGlobalInstance().getLastFocusedFrame();
205             if (frame == null || myProject == null || myProject == frame.getProject()) {
206               myToFocus.requestFocus();
207               if (shouldLogFocuses) {
208                 LOG.info("Force request focus on " + myToFocus.getClass().getName());
209               }
210             }
211           }
212         }
213         else if (shouldLogFocuses) {
214           LOG.info("We have successfully requested focus in window on " + myToFocus.getClass().getName());
215           LOG.info(myAllocation);
216         }
217         lastProcessedCommandTime = commandCreationTime;
218       }
219
220       clear();
221       return ActionCallback.DONE;
222     }
223
224     private void clear() {
225       myToFocus = null;
226       myDominationComponent = null;
227     }
228
229     @Override
230     public boolean isExpired() {
231       if (myToFocus == null) {
232         return true;
233       }
234       return false;
235     }
236
237     public Component getComponent() {
238       return myToFocus;
239     }
240
241     @Override
242     public boolean canFocusChangeFrom(@Nullable Component component) {
243       DialogWrapper dialog = DialogWrapper.findInstance(component);
244       return (dialog == null) || (dialog == DialogWrapper.findInstance(myToFocus));
245     }
246   }
247 }