1 package com
.intellij
.ide
;
4 import com
.intellij
.Patches
;
5 import com
.intellij
.concurrency
.JobSchedulerImpl
;
6 import com
.intellij
.ide
.dnd
.DnDManager
;
7 import com
.intellij
.ide
.dnd
.DnDManagerImpl
;
8 import com
.intellij
.openapi
.Disposable
;
9 import com
.intellij
.openapi
.application
.Application
;
10 import com
.intellij
.openapi
.application
.ApplicationManager
;
11 import com
.intellij
.openapi
.application
.ModalityState
;
12 import com
.intellij
.openapi
.application
.impl
.ApplicationImpl
;
13 import com
.intellij
.openapi
.diagnostic
.Logger
;
14 import com
.intellij
.openapi
.keymap
.impl
.IdeKeyEventDispatcher
;
15 import com
.intellij
.openapi
.keymap
.impl
.IdeMouseEventDispatcher
;
16 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
17 import com
.intellij
.openapi
.util
.Condition
;
18 import com
.intellij
.openapi
.util
.Disposer
;
19 import com
.intellij
.openapi
.util
.SystemInfo
;
20 import com
.intellij
.openapi
.util
.registry
.Registry
;
21 import com
.intellij
.openapi
.wm
.IdeFocusManager
;
22 import com
.intellij
.openapi
.wm
.ex
.WindowManagerEx
;
23 import com
.intellij
.util
.Alarm
;
24 import com
.intellij
.util
.ProfilingUtil
;
25 import com
.intellij
.util
.ReflectionUtil
;
26 import com
.intellij
.util
.containers
.ContainerUtil
;
27 import com
.intellij
.util
.containers
.HashMap
;
28 import org
.jetbrains
.annotations
.NotNull
;
32 import java
.awt
.event
.*;
33 import java
.beans
.PropertyChangeEvent
;
34 import java
.beans
.PropertyChangeListener
;
35 import java
.lang
.reflect
.Field
;
36 import java
.lang
.reflect
.Method
;
37 import java
.util
.ArrayList
;
38 import java
.util
.LinkedHashSet
;
44 * @author Vladimir Kondratyev
45 * @author Anton Katilin
48 public class IdeEventQueue
extends EventQueue
{
50 private static final Logger LOG
= Logger
.getInstance("#com.intellij.ide.IdeEventQueue");
52 private static final boolean DEBUG
= LOG
.isDebugEnabled();
55 * Adding/Removing of "idle" listeners should be thread safe.
57 private final Object myLock
= new Object();
59 private final ArrayList
<Runnable
> myIdleListeners
= new ArrayList
<Runnable
>(2);
61 private final ArrayList
<Runnable
> myActivityListeners
= new ArrayList
<Runnable
>(2);
63 private final Alarm myIdleRequestsAlarm
= new Alarm();
65 private final Alarm myIdleTimeCounterAlarm
= new Alarm(Alarm
.ThreadToUse
.SWING_THREAD
);
67 private long myIdleTime
;
69 private final Map
<Runnable
, MyFireIdleRequest
> myListener2Request
= new HashMap
<Runnable
, MyFireIdleRequest
>();
70 // IdleListener -> MyFireIdleRequest
72 private final IdeKeyEventDispatcher myKeyEventDispatcher
= new IdeKeyEventDispatcher();
74 private final IdeMouseEventDispatcher myMouseEventDispatcher
= new IdeMouseEventDispatcher();
76 private final IdePopupManager myPopupManager
= new IdePopupManager();
79 private boolean mySuspendMode
;
82 * We exit from suspend mode when focus owner changes and no more WindowEvent.WINDOW_OPENED events
84 * in the queue. If WINDOW_OPENED event does exists in the queus then we restart the alarm.
87 private Component myFocusOwner
;
89 private final Runnable myExitSuspendModeRunnable
= new ExitSuspendModeRunnable();
92 * We exit from suspend mode when this alarm is triggered and no mode WindowEvent.WINDOW_OPENED
94 * events in the queue. If WINDOW_OPENED event does exist then we restart the alarm.
96 private final Alarm mySuspendModeAlarm
= new Alarm();
99 * Counter of processed events. It is used to assert that data context lives only inside single
104 private int myEventCount
;
107 private boolean myIsInInputEvent
= false;
109 private AWTEvent myCurrentEvent
= null;
111 private long myLastActiveTime
;
113 private WindowManagerEx myWindowManager
;
116 private final Set
<EventDispatcher
> myDispatchers
= new LinkedHashSet
<EventDispatcher
>();
117 private final Set
<EventDispatcher
> myPostprocessors
= new LinkedHashSet
<EventDispatcher
>();
119 private static class IdeEventQueueHolder
{
120 private static final IdeEventQueue INSTANCE
= new IdeEventQueue();
123 public static IdeEventQueue
getInstance() {
124 return IdeEventQueueHolder
.INSTANCE
;
127 private IdeEventQueue() {
128 addIdleTimeCounterRequest();
129 final KeyboardFocusManager keyboardFocusManager
= KeyboardFocusManager
.getCurrentKeyboardFocusManager();
131 //noinspection HardCodedStringLiteral
132 keyboardFocusManager
.addPropertyChangeListener("permanentFocusOwner", new PropertyChangeListener() {
134 public void propertyChange(final PropertyChangeEvent e
) {
135 final Application application
= ApplicationManager
.getApplication();
136 if (application
== null) {
138 // We can get focus event before application is initialized
141 application
.assertIsDispatchThread();
142 final Window focusedWindow
= keyboardFocusManager
.getFocusedWindow();
143 final Component focusOwner
= keyboardFocusManager
.getFocusOwner();
144 if (mySuspendMode
&& focusedWindow
!= null && focusOwner
!= null && focusOwner
!= myFocusOwner
&& !(focusOwner
instanceof Window
)) {
152 public void setWindowManager(final WindowManagerEx windowManager
) {
153 myWindowManager
= windowManager
;
157 private void addIdleTimeCounterRequest() {
158 Application application
= ApplicationManager
.getApplication();
159 if (application
!= null && application
.isUnitTestMode()) return;
161 myIdleTimeCounterAlarm
.cancelAllRequests();
162 myLastActiveTime
= System
.currentTimeMillis();
163 myIdleTimeCounterAlarm
.addRequest(new Runnable() {
165 myIdleTime
+= System
.currentTimeMillis() - myLastActiveTime
;
166 addIdleTimeCounterRequest();
168 }, 20000, ModalityState
.NON_MODAL
);
172 public boolean shouldNotTypeInEditor() {
173 return myKeyEventDispatcher
.isWaitingForSecondKeyStroke() || mySuspendMode
;
177 private void enterSuspendMode() {
178 mySuspendMode
= true;
179 myFocusOwner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
180 mySuspendModeAlarm
.cancelAllRequests();
181 mySuspendModeAlarm
.addRequest(myExitSuspendModeRunnable
, 750);
186 * Exits supend mode and pumps all suspended events.
189 private void exitSuspendMode() {
190 if (shallEnterSuspendMode()) {
192 // We have to exit from suspend mode (focus owner changes or alarm is triggered) but
194 // WINDOW_OPENED isn't dispatched yet. In this case we have to restart the alarm until
196 // all WINDOW_OPENED event will be processed.
197 mySuspendModeAlarm
.cancelAllRequests();
198 mySuspendModeAlarm
.addRequest(myExitSuspendModeRunnable
, 250);
202 // Now we can pump all suspended events.
203 mySuspendMode
= false;
204 myFocusOwner
= null; // to prevent memory leaks
209 public void addIdleListener(@NotNull final Runnable runnable
, final int timeout
) {
210 LOG
.assertTrue(timeout
> 0);
211 synchronized (myLock
) {
212 myIdleListeners
.add(runnable
);
213 final MyFireIdleRequest request
= new MyFireIdleRequest(runnable
, timeout
);
214 myListener2Request
.put(runnable
, request
);
215 myIdleRequestsAlarm
.addRequest(request
, timeout
);
220 public void removeIdleListener(@NotNull final Runnable runnable
) {
221 synchronized (myLock
) {
222 final boolean wasRemoved
= myIdleListeners
.remove(runnable
);
224 LOG
.assertTrue(false, "unknown runnable: " + runnable
);
226 final MyFireIdleRequest request
= myListener2Request
.remove(runnable
);
227 LOG
.assertTrue(request
!= null);
228 myIdleRequestsAlarm
.cancelRequest(request
);
233 public void addActivityListener(@NotNull final Runnable runnable
) {
234 synchronized (myLock
) {
235 myActivityListeners
.add(runnable
);
239 public void addActivityListener(@NotNull final Runnable runnable
, Disposable parentDisposable
) {
240 synchronized (myLock
) {
241 ContainerUtil
.add(runnable
, myActivityListeners
, parentDisposable
);
246 public void removeActivityListener(@NotNull final Runnable runnable
) {
247 synchronized (myLock
) {
248 final boolean wasRemoved
= myActivityListeners
.remove(runnable
);
250 LOG
.assertTrue(false, "unknown runnable: " + runnable
);
256 public void addDispatcher(final EventDispatcher dispatcher
, Disposable parent
) {
257 _addProcessor(dispatcher
, parent
, myDispatchers
);
260 public void removeDispatcher(EventDispatcher dispatcher
) {
261 myDispatchers
.remove(dispatcher
);
264 public void addPostprocessor(EventDispatcher dispatcher
, Disposable parent
) {
265 _addProcessor(dispatcher
, parent
, myPostprocessors
);
268 public void removePostprocessor(EventDispatcher dispatcher
) {
269 myPostprocessors
.remove(dispatcher
);
272 private void _addProcessor(final EventDispatcher dispatcher
, Disposable parent
, Set
<EventDispatcher
> set
) {
274 if (parent
!= null) {
275 Disposer
.register(parent
, new Disposable() {
276 public void dispose() {
277 removeDispatcher(dispatcher
);
283 public int getEventCount() {
288 public void setEventCount(int evCount
) {
289 myEventCount
= evCount
;
293 public AWTEvent
getTrueCurrentEvent() {
294 return myCurrentEvent
;
297 //[jeka] commented for performance reasons
301 public void postEvent(final AWTEvent e) {
303 // [vova] sometime people call SwingUtilities.invokeLater(null). To
305 // find such situations we will specially check InvokationEvents
309 if (e instanceof InvocationEvent) {
311 //noinspection HardCodedStringLiteral
313 final Field field = InvocationEvent.class.getDeclaredField("runnable");
315 field.setAccessible(true);
317 final Object runnable = field.get(e);
319 if (runnable == null) {
321 //noinspection HardCodedStringLiteral
323 throw new IllegalStateException("InvocationEvent contains null runnable: " + e);
331 catch (final Exception exc) {
333 throw new Error(exc);
343 public void dispatchEvent(final AWTEvent e
) {
347 t
= System
.currentTimeMillis();
348 ProfilingUtil
.startCPUProfiling();
351 boolean wasInputEvent
= myIsInInputEvent
;
352 myIsInInputEvent
= e
instanceof InputEvent
|| e
instanceof InputMethodEvent
|| e
instanceof WindowEvent
|| e
instanceof ActionEvent
;
353 AWTEvent oldEvent
= myCurrentEvent
;
356 JobSchedulerImpl
.suspend();
361 myIsInInputEvent
= wasInputEvent
;
362 myCurrentEvent
= oldEvent
;
363 JobSchedulerImpl
.resume();
365 for (EventDispatcher each
: myPostprocessors
) {
370 final long processTime
= System
.currentTimeMillis() - t
;
371 if (processTime
> 100) {
372 final String path
= ProfilingUtil
.captureCPUSnapshot();
374 LOG
.debug("Long event: " + processTime
+ "ms - " + toDebugString(e
));
375 LOG
.debug("Snapshot taken: " + path
);
378 ProfilingUtil
.stopCPUProfiling();
384 @SuppressWarnings({"ALL"})
385 private static String
toDebugString(final AWTEvent e
) {
386 if (e
instanceof InvocationEvent
) {
388 final Field f
= InvocationEvent
.class.getDeclaredField("runnable");
389 f
.setAccessible(true);
390 Object runnable
= f
.get(e
);
392 return "Invoke Later[" + runnable
.toString() + "]";
394 catch (NoSuchFieldException e1
) {
396 catch (IllegalAccessException e1
) {
403 private void _dispatchEvent(final AWTEvent e
) {
404 if (e
.getID() == MouseEvent
.MOUSE_DRAGGED
) {
405 DnDManagerImpl dndManager
= (DnDManagerImpl
)DnDManager
.getInstance();
406 if (dndManager
!= null) {
407 dndManager
.setLastDropHandler(null);
415 if (processAppActivationEvents(e
)) return;
417 fixStickyFocusedComponents(e
);
419 if (!myPopupManager
.isPopupActive()) {
420 enterSuspendModeIfNeeded(e
);
423 if (typeAheadDispatchToFocusManager(e
)) return;
425 if (e
instanceof WindowEvent
) {
426 ActivityTracker
.getInstance().inc();
430 // Process "idle" and "activity" listeners
431 if (e
instanceof KeyEvent
|| e
instanceof MouseEvent
) {
432 ActivityTracker
.getInstance().inc();
434 synchronized (myLock
) {
435 myIdleRequestsAlarm
.cancelAllRequests();
436 for (Runnable idleListener
: myIdleListeners
) {
437 final MyFireIdleRequest request
= myListener2Request
.get(idleListener
);
438 if (request
== null) {
439 LOG
.error("There is no request for " + idleListener
);
441 int timeout
= request
.getTimeout();
442 myIdleRequestsAlarm
.addRequest(request
, timeout
, ModalityState
.NON_MODAL
);
444 if (KeyEvent
.KEY_PRESSED
== e
.getID() ||
445 KeyEvent
.KEY_TYPED
== e
.getID() ||
446 MouseEvent
.MOUSE_PRESSED
== e
.getID() ||
447 MouseEvent
.MOUSE_RELEASED
== e
.getID() ||
448 MouseEvent
.MOUSE_CLICKED
== e
.getID()) {
449 addIdleTimeCounterRequest();
450 for (Runnable activityListener
: myActivityListeners
) {
451 activityListener
.run();
456 if (myPopupManager
.isPopupActive() && myPopupManager
.dispatch(e
)) {
460 for (EventDispatcher eachDispatcher
: myDispatchers
) {
461 if (eachDispatcher
.dispatch(e
)) {
466 if (e
instanceof InputMethodEvent
) {
467 if (SystemInfo
.isMac
&& myKeyEventDispatcher
.isWaitingForSecondKeyStroke()) {
471 if (e
instanceof InputEvent
&& Patches
.SPECIAL_WINPUT_METHOD_PROCESSING
) {
472 final InputEvent inputEvent
= (InputEvent
)e
;
473 if (!inputEvent
.getComponent().isShowing()) {
477 if (e
instanceof ComponentEvent
&& myWindowManager
!= null) {
478 myWindowManager
.dispatchComponentEvent((ComponentEvent
)e
);
480 if (e
instanceof KeyEvent
) {
481 if (mySuspendMode
|| !myKeyEventDispatcher
.dispatchKeyEvent((KeyEvent
)e
)) {
482 defaultDispatchEvent(e
);
485 ((KeyEvent
)e
).consume();
486 defaultDispatchEvent(e
);
489 else if (e
instanceof MouseEvent
) {
490 if (!myMouseEventDispatcher
.dispatchMouseEvent((MouseEvent
)e
)) {
491 defaultDispatchEvent(e
);
495 defaultDispatchEvent(e
);
499 private void fixStickyFocusedComponents(AWTEvent e
) {
500 if (!(e
instanceof InputEvent
)) return;
502 final KeyboardFocusManager mgr
= KeyboardFocusManager
.getCurrentKeyboardFocusManager();
503 final Window wnd
= mgr
.getActiveWindow();
504 Window showingWindow
= wnd
;
506 if (Registry
.is("actionSystem.fixStickyFocusedWindows")) {
507 if (wnd
!= null && !wnd
.isShowing()) {
508 while (showingWindow
!= null) {
509 if (showingWindow
.isShowing()) break;
510 showingWindow
= (Window
)showingWindow
.getParent();
513 if (showingWindow
== null) {
514 final Frame
[] allFrames
= Frame
.getFrames();
515 for (Frame each
: allFrames
) {
516 if (each
.isShowing()) {
517 showingWindow
= each
;
524 if (showingWindow
!= null && showingWindow
!= wnd
) {
525 final Method setActive
=
526 ReflectionUtil
.findMethod(KeyboardFocusManager
.class.getDeclaredMethods(), "setGlobalActiveWindow", Window
.class);
527 if (setActive
!= null) {
529 setActive
.setAccessible(true);
530 setActive
.invoke(mgr
, (Window
)showingWindow
);
532 catch (Exception exc
) {
540 if (Registry
.is("actionSystem.fixNullFocusedComponent")) {
541 final Component focusOwner
= mgr
.getFocusOwner();
542 if (focusOwner
== null) {
543 if (showingWindow
!= null) {
544 final IdeFocusManager fm
= IdeFocusManager
.findInstanceByComponent(showingWindow
);
545 fm
.doWhenFocusSettlesDown(new Runnable() {
547 if (mgr
.getFocusOwner() == null) {
548 final Application app
= ApplicationManager
.getApplication();
549 if (app
!= null && app
.isActive()) {
550 fm
.requestDefaultFocus(false);
560 private void enterSuspendModeIfNeeded(AWTEvent e
) {
561 if (e
instanceof KeyEvent
) {
562 if (!mySuspendMode
&& shallEnterSuspendMode()) {
568 private boolean shallEnterSuspendMode() {
569 return peekEvent(WindowEvent
.WINDOW_OPENED
) != null;
572 private static boolean processAppActivationEvents(AWTEvent e
) {
573 final Application app
= ApplicationManager
.getApplication();
574 if (!(app
instanceof ApplicationImpl
)) return false;
576 ApplicationImpl appImpl
= (ApplicationImpl
)app
;
578 boolean consumed
= false;
579 if (e
instanceof WindowEvent
) {
580 WindowEvent we
= (WindowEvent
)e
;
581 if (we
.getID() == WindowEvent
.WINDOW_GAINED_FOCUS
&& we
.getWindow() != null) {
582 if (we
.getOppositeWindow() == null && !appImpl
.isActive()) {
583 consumed
= appImpl
.tryToApplyActivationState(true, we
.getWindow());
586 else if (we
.getID() == WindowEvent
.WINDOW_LOST_FOCUS
&& we
.getWindow() != null) {
587 if (we
.getOppositeWindow() == null && appImpl
.isActive()) {
588 consumed
= appImpl
.tryToApplyActivationState(false, we
.getWindow());
593 return consumed
&& Patches
.REQUEST_FOCUS_MAY_ACTIVATE_APP
;
597 private void defaultDispatchEvent(final AWTEvent e
) {
599 super.dispatchEvent(e
);
601 catch (ProcessCanceledException pce
) {
604 catch (Throwable exc
) {
605 LOG
.error("Error during dispatching of " + e
, exc
);
609 private static boolean typeAheadDispatchToFocusManager(AWTEvent e
) {
610 if (e
instanceof KeyEvent
) {
611 final KeyEvent event
= (KeyEvent
)e
;
612 if (!event
.isConsumed()) {
613 final IdeFocusManager focusManager
= IdeFocusManager
.findInstanceByComponent(event
.getComponent());
614 return focusManager
.dispatch(event
);
622 public void flushQueue() {
624 AWTEvent event
= peekEvent();
625 if (event
== null) return;
627 AWTEvent event1
= getNextEvent();
628 dispatchEvent(event1
);
630 catch (Exception e
) {
636 public void pumpEventsForHierarchy(Component modalComponent
, Condition
<AWTEvent
> exitCondition
) {
640 event
= getNextEvent();
641 boolean eventOk
= true;
642 if (event
instanceof InputEvent
) {
643 final Object s
= event
.getSource();
644 if (s
instanceof Component
) {
645 Component c
= (Component
)s
;
646 Window modalWindow
= SwingUtilities
.windowForComponent(modalComponent
);
647 while (c
!= null && c
!= modalWindow
) c
= c
.getParent();
650 ((InputEvent
)event
).consume();
656 dispatchEvent(event
);
659 catch (Throwable e
) {
664 while (!exitCondition
.value(event
));
668 public interface EventDispatcher
{
669 boolean dispatch(AWTEvent e
);
673 private final class MyFireIdleRequest
implements Runnable
{
674 private final Runnable myRunnable
;
675 private final int myTimeout
;
678 public MyFireIdleRequest(final Runnable runnable
, final int timeout
) {
680 myRunnable
= runnable
;
686 synchronized (myLock
) {
687 myIdleRequestsAlarm
.addRequest(this, myTimeout
, ModalityState
.NON_MODAL
);
691 public int getTimeout() {
697 private final class ExitSuspendModeRunnable
implements Runnable
{
707 public long getIdleTime() {
712 public IdePopupManager
getPopupManager() {
713 return myPopupManager
;
716 public IdeKeyEventDispatcher
getKeyEventDispatcher() {
717 return myKeyEventDispatcher
;
720 public void blockNextEvents(final MouseEvent e
) {
721 myMouseEventDispatcher
.blockNextEvents(e
);
724 public boolean isSuspendMode() {
725 return mySuspendMode
;