cd60e1fb562e56b20634ca1a3fd43da74b64f8c9
[idea/community.git] / platform / platform-impl / src / com / intellij / ide / IdeEventQueue.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.ide;
17
18 import com.intellij.ide.dnd.DnDManager;
19 import com.intellij.ide.dnd.DnDManagerImpl;
20 import com.intellij.ide.plugins.PluginManager;
21 import com.intellij.ide.ui.UISettings;
22 import com.intellij.idea.IdeaApplication;
23 import com.intellij.openapi.Disposable;
24 import com.intellij.openapi.application.Application;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.application.ModalityState;
27 import com.intellij.openapi.application.ex.ApplicationEx;
28 import com.intellij.openapi.application.impl.LaterInvocator;
29 import com.intellij.openapi.diagnostic.FrequentEventDetector;
30 import com.intellij.openapi.diagnostic.Logger;
31 import com.intellij.openapi.keymap.KeyboardSettingsExternalizable;
32 import com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher;
33 import com.intellij.openapi.keymap.impl.IdeMouseEventDispatcher;
34 import com.intellij.openapi.keymap.impl.KeyState;
35 import com.intellij.openapi.util.Condition;
36 import com.intellij.openapi.util.Disposer;
37 import com.intellij.openapi.util.ExpirableRunnable;
38 import com.intellij.openapi.util.SystemInfo;
39 import com.intellij.openapi.util.registry.Registry;
40 import com.intellij.openapi.wm.IdeFocusManager;
41 import com.intellij.openapi.wm.IdeFrame;
42 import com.intellij.openapi.wm.WindowManager;
43 import com.intellij.openapi.wm.ex.WindowManagerEx;
44 import com.intellij.openapi.wm.impl.FocusManagerImpl;
45 import com.intellij.util.Alarm;
46 import com.intellij.util.ReflectionUtil;
47 import com.intellij.util.containers.ContainerUtil;
48 import com.intellij.util.containers.HashMap;
49 import com.intellij.util.ui.MouseEventAdapter;
50 import com.intellij.util.ui.UIUtil;
51 import org.jetbrains.annotations.NotNull;
52 import org.jetbrains.annotations.Nullable;
53
54 import javax.swing.*;
55 import javax.swing.plaf.basic.ComboPopup;
56 import java.awt.*;
57 import java.awt.event.*;
58 import java.beans.PropertyChangeEvent;
59 import java.beans.PropertyChangeListener;
60 import java.lang.reflect.Field;
61 import java.lang.reflect.Method;
62 import java.util.LinkedHashSet;
63 import java.util.List;
64 import java.util.Map;
65 import java.util.Set;
66
67 /**
68  * @author Vladimir Kondratyev
69  * @author Anton Katilin
70  */
71 public class IdeEventQueue extends EventQueue {
72   private static final Logger LOG = Logger.getInstance("#com.intellij.ide.IdeEventQueue");
73
74   /**
75    * Adding/Removing of "idle" listeners should be thread safe.
76    */
77   private final Object myLock = new Object();
78
79   private final List<Runnable> myIdleListeners = ContainerUtil.createLockFreeCopyOnWriteList();
80
81   private final List<Runnable> myActivityListeners = ContainerUtil.createLockFreeCopyOnWriteList();
82
83   private final Alarm myIdleRequestsAlarm = new Alarm();
84
85   private final Alarm myIdleTimeCounterAlarm = new Alarm();
86
87   private long myIdleTime;
88
89   private final Map<Runnable, MyFireIdleRequest> myListener2Request = new HashMap<Runnable, MyFireIdleRequest>();
90   // IdleListener -> MyFireIdleRequest
91
92   private final IdeKeyEventDispatcher myKeyEventDispatcher = new IdeKeyEventDispatcher(this);
93
94   private final IdeMouseEventDispatcher myMouseEventDispatcher = new IdeMouseEventDispatcher();
95
96   private final IdePopupManager myPopupManager = new IdePopupManager();
97
98
99   private final ToolkitBugsProcessor myToolkitBugsProcessor = new ToolkitBugsProcessor();
100
101   private boolean mySuspendMode;
102
103   /**
104    * We exit from suspend mode when focus owner changes and no more WindowEvent.WINDOW_OPENED events
105    * <p/>
106    * in the queue. If WINDOW_OPENED event does exists in the queues then we restart the alarm.
107    */
108   private Component myFocusOwner;
109
110   private final Runnable myExitSuspendModeRunnable = new ExitSuspendModeRunnable();
111
112   /**
113    * We exit from suspend mode when this alarm is triggered and no mode WindowEvent.WINDOW_OPENED
114    * <p/>
115    * events in the queue. If WINDOW_OPENED event does exist then we restart the alarm.
116    */
117   private final Alarm mySuspendModeAlarm = new Alarm();
118
119   /**
120    * Counter of processed events. It is used to assert that data context lives only inside single
121    * <p/>
122    * Swing event.
123    */
124   private int myEventCount;
125
126   private boolean myIsInInputEvent;
127
128   private AWTEvent myCurrentEvent;
129
130   private long myLastActiveTime;
131
132   private WindowManagerEx myWindowManager;
133
134   private final Set<EventDispatcher> myDispatchers = new LinkedHashSet<EventDispatcher>();
135   private final Set<EventDispatcher> myPostProcessors = new LinkedHashSet<EventDispatcher>();
136   private final Set<Runnable> myReady = ContainerUtil.newHashSet();
137
138   private boolean myKeyboardBusy;
139   private boolean myDispatchingFocusEvent;
140
141   private int myInputMethodLock;
142
143   private static class IdeEventQueueHolder {
144     private static final IdeEventQueue INSTANCE = new IdeEventQueue();
145   }
146
147   public static IdeEventQueue getInstance() {
148     return IdeEventQueueHolder.INSTANCE;
149   }
150
151   private IdeEventQueue() {
152     EventQueue systemEventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
153     assert !(systemEventQueue instanceof IdeEventQueue) : systemEventQueue;
154     systemEventQueue.push(this);
155     addIdleTimeCounterRequest();
156
157     final KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
158     keyboardFocusManager.addPropertyChangeListener("permanentFocusOwner", new PropertyChangeListener() {
159
160       @Override
161       public void propertyChange(@NotNull final PropertyChangeEvent e) {
162         final Application application = ApplicationManager.getApplication();
163         if (application == null) {
164           // We can get focus event before application is initialized
165           return;
166         }
167         application.assertIsDispatchThread();
168         final Window focusedWindow = keyboardFocusManager.getFocusedWindow();
169         final Component focusOwner = keyboardFocusManager.getFocusOwner();
170         if (mySuspendMode && focusedWindow != null && focusOwner != null && focusOwner != myFocusOwner && !(focusOwner instanceof Window)) {
171           exitSuspendMode();
172         }
173       }
174     });
175
176     addDispatcher(new WindowsAltSuppressor(), null);
177   }
178
179
180   public void setWindowManager(final WindowManagerEx windowManager) {
181     myWindowManager = windowManager;
182   }
183
184
185   private void addIdleTimeCounterRequest() {
186     if (isTestMode()) return;
187
188     myIdleTimeCounterAlarm.cancelAllRequests();
189     myLastActiveTime = System.currentTimeMillis();
190     myIdleTimeCounterAlarm.addRequest(new Runnable() {
191       @Override
192       public void run() {
193         myIdleTime += System.currentTimeMillis() - myLastActiveTime;
194         addIdleTimeCounterRequest();
195       }
196     }, 20000, ModalityState.NON_MODAL);
197   }
198
199   /**
200    * This class performs special processing in order to have {@link #getIdleTime()} return more or less up-to-date data.
201    * <p/>
202    * This method allows to stop that processing (convenient in non-intellij environment like upsource).
203    */
204   @SuppressWarnings("unused") // Used in upsource.
205   public void stopIdleTimeCalculation() {
206     myIdleTimeCounterAlarm.cancelAllRequests();
207   }
208
209   public boolean shouldNotTypeInEditor() {
210     return myKeyEventDispatcher.isWaitingForSecondKeyStroke() || mySuspendMode;
211   }
212
213
214   private void enterSuspendMode() {
215     mySuspendMode = true;
216     myFocusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
217     mySuspendModeAlarm.cancelAllRequests();
218     mySuspendModeAlarm.addRequest(myExitSuspendModeRunnable, 750);
219   }
220
221   /**
222    * Exits suspend mode and pumps all suspended events.
223    */
224   private void exitSuspendMode() {
225     if (shallEnterSuspendMode()) {
226       // We have to exit from suspend mode (focus owner changes or alarm is triggered) but
227       // WINDOW_OPENED isn't dispatched yet. In this case we have to restart the alarm until
228       // all WINDOW_OPENED event will be processed.
229       mySuspendModeAlarm.cancelAllRequests();
230       mySuspendModeAlarm.addRequest(myExitSuspendModeRunnable, 250);
231     }
232     else {
233       // Now we can pump all suspended events.
234       mySuspendMode = false;
235       myFocusOwner = null; // to prevent memory leaks
236     }
237   }
238
239
240   public void addIdleListener(@NotNull final Runnable runnable, final int timeout) {
241     LOG.assertTrue(timeout > 0);
242     synchronized (myLock) {
243       myIdleListeners.add(runnable);
244       final MyFireIdleRequest request = new MyFireIdleRequest(runnable, timeout);
245       myListener2Request.put(runnable, request);
246       myIdleRequestsAlarm.addRequest(request, timeout);
247     }
248   }
249
250   public void removeIdleListener(@NotNull final Runnable runnable) {
251     synchronized (myLock) {
252       final boolean wasRemoved = myIdleListeners.remove(runnable);
253       if (!wasRemoved) {
254         LOG.error("unknown runnable: " + runnable);
255       }
256       final MyFireIdleRequest request = myListener2Request.remove(runnable);
257       LOG.assertTrue(request != null);
258       myIdleRequestsAlarm.cancelRequest(request);
259     }
260   }
261
262   /** @deprecated use {@link #addActivityListener(Runnable, Disposable)} (to be removed in IDEA 17) */
263   @SuppressWarnings("unused")
264   public void addActivityListener(@NotNull final Runnable runnable) {
265     synchronized (myLock) {
266       myActivityListeners.add(runnable);
267     }
268   }
269
270   public void addActivityListener(@NotNull final Runnable runnable, Disposable parentDisposable) {
271     synchronized (myLock) {
272       ContainerUtil.add(runnable, myActivityListeners, parentDisposable);
273     }
274   }
275
276   public void removeActivityListener(@NotNull final Runnable runnable) {
277     synchronized (myLock) {
278       myActivityListeners.remove(runnable);
279     }
280   }
281
282
283   public void addDispatcher(final EventDispatcher dispatcher, Disposable parent) {
284     _addProcessor(dispatcher, parent, myDispatchers);
285   }
286
287   public void removeDispatcher(EventDispatcher dispatcher) {
288     myDispatchers.remove(dispatcher);
289   }
290
291   public boolean containsDispatcher(EventDispatcher dispatcher) {
292     return myDispatchers.contains(dispatcher);
293   }
294
295   public void addPostprocessor(EventDispatcher dispatcher, @Nullable Disposable parent) {
296     _addProcessor(dispatcher, parent, myPostProcessors);
297   }
298
299   public void removePostprocessor(EventDispatcher dispatcher) {
300     myPostProcessors.remove(dispatcher);
301   }
302
303   private static void _addProcessor(final EventDispatcher dispatcher, Disposable parent, final Set<EventDispatcher> set) {
304     set.add(dispatcher);
305     if (parent != null) {
306       Disposer.register(parent, new Disposable() {
307         @Override
308         public void dispose() {
309           set.remove(dispatcher);
310         }
311       });
312     }
313   }
314
315   public int getEventCount() {
316     return myEventCount;
317   }
318
319   public void setEventCount(int evCount) {
320     myEventCount = evCount;
321   }
322
323   public AWTEvent getTrueCurrentEvent() {
324     return myCurrentEvent;
325   }
326
327   private static class InertialMouseRouter {
328     private static final int MOUSE_WHEEL_RESTART_THRESHOLD = 50;
329     private static Component wheelDestinationComponent;
330     private static long lastMouseWheel;
331
332     private static AWTEvent changeSourceIfNeeded(AWTEvent awtEvent) {
333       if (SystemInfo.isMac && Registry.is("ide.inertial.mouse.fix") && awtEvent instanceof MouseWheelEvent) {
334         MouseWheelEvent mwe = (MouseWheelEvent) awtEvent;
335         if (mwe.getWhen() - lastMouseWheel > MOUSE_WHEEL_RESTART_THRESHOLD) {
336           wheelDestinationComponent = SwingUtilities.getDeepestComponentAt(mwe.getComponent(), mwe.getX(), mwe.getY());
337         }
338         lastMouseWheel = System.currentTimeMillis();
339
340         int modifiers = mwe.getModifiers() | mwe.getModifiersEx();
341         return MouseEventAdapter.convert(mwe, wheelDestinationComponent, mwe.getID(), lastMouseWheel, modifiers, mwe.getX(), mwe.getY());
342       }
343       return awtEvent;
344     }
345   }
346
347   private static boolean ourAppIsLoaded;
348
349   private static boolean appIsLoaded() {
350     if (ourAppIsLoaded) return true;
351     boolean loaded = IdeaApplication.isLoaded();
352     if (loaded) ourAppIsLoaded = true;
353     return loaded;
354   }
355
356   @Override
357   public void dispatchEvent(@NotNull AWTEvent e) {
358     if (!appIsLoaded()) {
359       try {
360         super.dispatchEvent(e);
361       }
362       catch (Throwable t) {
363         processException(t);
364       }
365       return;
366     }
367
368     e = InertialMouseRouter.changeSourceIfNeeded(e);
369
370     e = fixNonEnglishKeyboardLayouts(e);
371
372     e = mapEvent(e);
373
374     boolean wasInputEvent = myIsInInputEvent;
375     myIsInInputEvent = e instanceof InputEvent || e instanceof InputMethodEvent || e instanceof WindowEvent || e instanceof ActionEvent;
376     AWTEvent oldEvent = myCurrentEvent;
377     myCurrentEvent = e;
378
379     try {
380       _dispatchEvent(e, false);
381     }
382     catch (Throwable t) {
383       processException(t);
384     }
385     finally {
386       myIsInInputEvent = wasInputEvent;
387       myCurrentEvent = oldEvent;
388
389       for (EventDispatcher each : myPostProcessors) {
390         each.dispatch(e);
391       }
392
393       if (e instanceof KeyEvent) {
394         maybeReady();
395       }
396     }
397   }
398
399   private void processException(Throwable t) {
400     if (!myToolkitBugsProcessor.process(t)) {
401       PluginManager.processException(t);
402     }
403   }
404
405   private static int ctrlIsPressedCount;
406   private static boolean leftAltIsPressed;
407   //private static boolean altGrIsPressed = false;
408
409   private static AWTEvent fixNonEnglishKeyboardLayouts(AWTEvent e) {
410     if (!(e instanceof KeyEvent)) return e;
411
412     KeyboardSettingsExternalizable externalizable = KeyboardSettingsExternalizable.getInstance();
413     if (externalizable == null || !externalizable.isNonEnglishKeyboardSupportEnabled()) return e;
414
415     KeyEvent ke = (KeyEvent)e;
416
417     // Try to get it from editor
418     Component sourceComponent = WindowManagerEx.getInstanceEx().getMostRecentFocusedWindow();
419
420     if (ke.getID() == KeyEvent.KEY_PRESSED) {
421       switch (ke.getKeyCode()) {
422         case KeyEvent.VK_CONTROL:
423           if ((ke.getModifiersEx() & (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) != (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) {
424             ctrlIsPressedCount++;
425           }
426           break;
427         case KeyEvent.VK_ALT:
428           if (ke.getKeyLocation() == KeyEvent.KEY_LOCATION_LEFT) {
429             if ((ke.getModifiersEx() & (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) != (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) {
430               leftAltIsPressed = true;
431             }
432           }
433           break;
434       }
435     }
436     else if (ke.getID() == KeyEvent.KEY_RELEASED) {
437       switch (ke.getKeyCode()) {
438         case KeyEvent.VK_CONTROL:
439           ctrlIsPressedCount--;
440           break;
441         case KeyEvent.VK_ALT:
442           if (ke.getKeyLocation() == KeyEvent.KEY_LOCATION_LEFT) {
443             leftAltIsPressed = false;
444           }
445           break;
446       }
447     }
448
449     if (!leftAltIsPressed && KeyboardSettingsExternalizable.getInstance().isUkrainianKeyboard(sourceComponent)) {
450       if ('ґ' == ke.getKeyChar() || ke.getKeyCode() == KeyEvent.VK_U) {
451         ke = new KeyEvent(ke.getComponent(), ke.getID(), ke.getWhen(), 0,
452                           KeyEvent.VK_UNDEFINED, 'ґ', ke.getKeyLocation());
453         ke.setKeyCode(KeyEvent.VK_U);
454         ke.setKeyChar('ґ');
455         return ke;
456       }
457     }
458
459     Integer keyCodeFromChar = CharToVKeyMap.get(ke.getKeyChar());
460     if (keyCodeFromChar != null) {
461       if (keyCodeFromChar != ke.getKeyCode()) {
462         // non-english layout
463         ke.setKeyCode(keyCodeFromChar);
464       }
465
466       //for (int i = 0; sourceComponent == null && i < WindowManagerEx.getInstanceEx().getAllProjectFrames().length; i++) {
467       //  sourceComponent = WindowManagerEx.getInstanceEx().getAllProjectFrames()[i].getComponent();
468       //}
469
470       if (sourceComponent != null) {
471         if (KeyboardSettingsExternalizable.isSupportedKeyboardLayout(sourceComponent)) {
472           if ((ke.getModifiersEx() & (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) != 0 /*&& ke.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT*/) {
473             // On German keyboard layout on Windows  we are getting on key press
474             // ctrl + alt instead of AltGr
475
476             int modifiers = ke.getModifiersEx() ^ InputEvent.ALT_DOWN_MASK ^ InputEvent.CTRL_DOWN_MASK;
477
478             if (ctrlIsPressedCount > 1) {
479               modifiers |= InputEvent.CTRL_DOWN_MASK;
480             }
481
482             if (leftAltIsPressed) {
483               modifiers |= InputEvent.ALT_MASK;
484             }
485
486             int oldKeyCode = ke.getKeyCode();
487
488             //noinspection MagicConstant
489             ke = new KeyEvent(ke.getComponent(), ke.getID(), ke.getWhen(), modifiers,
490                              KeyEvent.VK_UNDEFINED, ke.getKeyChar(), KeyEvent.KEY_LOCATION_UNKNOWN);
491
492             ke.setKeyCode(oldKeyCode);
493           }
494         }
495       }
496     }
497
498     return ke;
499   }
500
501   private static AWTEvent mapEvent(AWTEvent e) {
502     if (SystemInfo.isXWindow && e instanceof MouseEvent && ((MouseEvent)e).getButton() > 3) {
503       MouseEvent src = (MouseEvent)e;
504       if (src.getButton() < 6) {
505         // Convert these events(buttons 4&5 in are produced by touchpad, they must be converted to horizontal scrolling events
506         e = new MouseWheelEvent(src.getComponent(), src.getID(), src.getWhen(),
507                                 src.getModifiers() | InputEvent.SHIFT_DOWN_MASK, src.getX(), src.getY(),
508                                 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, src.getClickCount(), src.getButton() == 4 ? -1 : 1);
509       }
510       else {
511         // Here we "shift" events with buttons 6 and 7 to similar events with buttons 4 and 5
512         // See java.awt.InputEvent#BUTTON_DOWN_MASK, 1<<14 is 4th physical button, 1<<15 is 5th.
513         //noinspection MagicConstant
514         e = new MouseEvent(src.getComponent(), src.getID(), src.getWhen(), src.getModifiers() | (1 << 8 + src.getButton()),
515                            src.getX(), src.getY(), 1, src.isPopupTrigger(), src.getButton() - 2);
516       }
517     }
518     return e;
519   }
520
521   public void _dispatchEvent(@NotNull AWTEvent e, boolean typeAheadFlushing) {
522     if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
523       DnDManagerImpl dndManager = (DnDManagerImpl)DnDManager.getInstance();
524       if (dndManager != null) {
525         dndManager.setLastDropHandler(null);
526       }
527     }
528
529     myEventCount++;
530     
531     traceClipboardEvents(e);
532
533     if (processAppActivationEvents(e)) return;
534
535     if (!typeAheadFlushing) {
536       fixStickyFocusedComponents(e);
537     }
538
539     if (!myPopupManager.isPopupActive()) {
540       enterSuspendModeIfNeeded(e);
541     }
542
543     myKeyboardBusy = e instanceof KeyEvent ||
544                      peekEvent(KeyEvent.KEY_PRESSED) != null ||
545                      peekEvent(KeyEvent.KEY_RELEASED) != null ||
546                      peekEvent(KeyEvent.KEY_TYPED) != null;
547
548     if (e instanceof KeyEvent) {
549       if (e.getID() == KeyEvent.KEY_RELEASED && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_SHIFT) {
550         myMouseEventDispatcher.resetHorScrollingTracker();
551       }
552     }
553
554     if (!typeAheadFlushing && typeAheadDispatchToFocusManager(e)) {
555       return;
556     }
557
558     if (e instanceof WindowEvent) {
559       ActivityTracker.getInstance().inc();
560     }
561
562     if (e instanceof MouseWheelEvent) {
563       final MenuElement[] selectedPath = MenuSelectionManager.defaultManager().getSelectedPath();
564       if (selectedPath.length > 0 && !(selectedPath[0] instanceof ComboPopup)) {
565         ((MouseWheelEvent)e).consume();
566         return;
567       }
568     }
569
570
571     // Process "idle" and "activity" listeners
572     if (e instanceof KeyEvent || e instanceof MouseEvent) {
573       ActivityTracker.getInstance().inc();
574
575       synchronized (myLock) {
576         myIdleRequestsAlarm.cancelAllRequests();
577         for (Runnable idleListener : myIdleListeners) {
578           final MyFireIdleRequest request = myListener2Request.get(idleListener);
579           if (request == null) {
580             LOG.error("There is no request for " + idleListener);
581           }
582           else {
583             myIdleRequestsAlarm.addRequest(request, request.getTimeout(), ModalityState.NON_MODAL);
584           }
585         }
586         if (KeyEvent.KEY_PRESSED == e.getID() ||
587             KeyEvent.KEY_TYPED == e.getID() ||
588             MouseEvent.MOUSE_PRESSED == e.getID() ||
589             MouseEvent.MOUSE_RELEASED == e.getID() ||
590             MouseEvent.MOUSE_CLICKED == e.getID()) {
591           addIdleTimeCounterRequest();
592           for (Runnable activityListener : myActivityListeners) {
593             activityListener.run();
594           }
595         }
596       }
597     }
598     if (myPopupManager.isPopupActive() && myPopupManager.dispatch(e)) {
599       if (myKeyEventDispatcher.isWaitingForSecondKeyStroke()) {
600         myKeyEventDispatcher.setState(KeyState.STATE_INIT);
601       }
602
603       return;
604     }
605
606     for (EventDispatcher eachDispatcher : myDispatchers) {
607       if (eachDispatcher.dispatch(e)) {
608         return;
609       }
610     }
611
612     if (e instanceof InputMethodEvent) {
613       if (SystemInfo.isMac && myKeyEventDispatcher.isWaitingForSecondKeyStroke()) {
614         return;
615       }
616     }
617     if (e instanceof ComponentEvent && myWindowManager != null) {
618       myWindowManager.dispatchComponentEvent((ComponentEvent)e);
619     }
620     if (e instanceof KeyEvent) {
621       if (mySuspendMode || !myKeyEventDispatcher.dispatchKeyEvent((KeyEvent)e)) {
622         defaultDispatchEvent(e);
623       }
624       else {
625         ((KeyEvent)e).consume();
626         defaultDispatchEvent(e);
627       }
628     }
629     else if (e instanceof MouseEvent) {
630       MouseEvent me = (MouseEvent)e;
631       if (IdeMouseEventDispatcher.patchClickCount(me) && me.getID() == MouseEvent.MOUSE_CLICKED) {
632         final MouseEvent toDispatch =
633           new MouseEvent(me.getComponent(), me.getID(), System.currentTimeMillis(), me.getModifiers(), me.getX(), me.getY(), 1,
634                          me.isPopupTrigger(), me.getButton());
635         //noinspection SSBasedInspection
636         SwingUtilities.invokeLater(new Runnable() {
637           @Override
638           public void run() {
639             dispatchEvent(toDispatch);
640           }
641         });
642       }
643       if (!myMouseEventDispatcher.dispatchMouseEvent(me)) {
644         defaultDispatchEvent(e);
645       }
646     }
647     else {
648       defaultDispatchEvent(e);
649     }
650   }
651
652   private static final Field ourInvocationEventRunnableAccessor;
653   
654   static {
655     Field field = null;
656     if (Registry.is("trace.clipboard.events") && SystemInfo.isJavaVersionAtLeast("1.8.0_60")) {
657       try {
658         field = InvocationEvent.class.getDeclaredField("runnable");
659         field.setAccessible(true);
660       }
661       catch (Exception e) {
662         LOG.warn("Error creating accessor for java.awt.event.InvocationEvent.runnable field", e);
663       }
664     }
665     ourInvocationEventRunnableAccessor = field;
666   }
667   
668   private static void traceClipboardEvents(AWTEvent e) {
669     if (ourInvocationEventRunnableAccessor != null && e instanceof InvocationEvent && e.getClass().getName().equals("sun.awt.PeerEvent")) {
670       try {
671         Object r = ourInvocationEventRunnableAccessor.get(e);
672         if (r != null) {
673           String className = r.getClass().getName();
674           // This check for event constructed in SunClipboard.lostOwnershipLater() (known to work with JDK 8u60) 
675           if (className.contains("sun.awt.datatransfer.SunClipboard") && className.contains("Lambda")) {
676             LOG.info("Clipboard has been set by other application");
677           }
678         }
679       }
680       catch (Exception ex) {
681         LOG.warn("Error accessing java.awt.event.InvocationEvent.runnable field");
682       }
683     }
684   }
685
686   private static void fixStickyWindow(KeyboardFocusManager mgr, Window wnd, String resetMethod) {
687     Window showingWindow = wnd;
688
689     if (wnd != null && !wnd.isShowing()) {
690       while (showingWindow != null) {
691         if (showingWindow.isShowing()) break;
692         showingWindow = (Window)showingWindow.getParent();
693       }
694
695       if (showingWindow == null) {
696         final Frame[] allFrames = Frame.getFrames();
697         for (Frame each : allFrames) {
698           if (each.isShowing()) {
699             showingWindow = each;
700             break;
701           }
702         }
703       }
704
705
706       if (showingWindow != null && showingWindow != wnd) {
707         final Method setActive = ReflectionUtil.findMethod(ReflectionUtil.getClassDeclaredMethods(KeyboardFocusManager.class, false), resetMethod, Window.class);
708         if (setActive != null) {
709           try {
710             setActive.invoke(mgr, (Window)showingWindow);
711           }
712           catch (Exception exc) {
713             LOG.info(exc);
714           }
715         }
716       }
717     }
718   }
719
720   public void fixStickyFocusedComponents(@Nullable AWTEvent e) {
721     if (e != null && !(e instanceof InputEvent)) return;
722
723     final KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
724
725     if (Registry.is("actionSystem.fixStickyFocusedWindows")) {
726       fixStickyWindow(mgr, mgr.getActiveWindow(), "setGlobalActiveWindow");
727       fixStickyWindow(mgr, mgr.getFocusedWindow(), "setGlobalFocusedWindow");
728     }
729
730     if (Registry.is("actionSystem.fixNullFocusedComponent")) {
731       final Component focusOwner = mgr.getFocusOwner();
732       if (focusOwner == null || !focusOwner.isShowing() || focusOwner instanceof JFrame || focusOwner instanceof JDialog) {
733
734         final Application app = ApplicationManager.getApplication();
735         if (app instanceof ApplicationEx && !((ApplicationEx) app).isLoaded()) {
736           return;
737         }
738
739         boolean mouseEventsAhead = isMouseEventAhead(e);
740         boolean focusTransferredNow = IdeFocusManager.getGlobalInstance().isFocusBeingTransferred();
741
742         boolean okToFixFocus = !mouseEventsAhead && !focusTransferredNow;
743
744         if (okToFixFocus) {
745           Window showingWindow = mgr.getActiveWindow();
746           if (showingWindow == null) {
747             Method getNativeFocusOwner = ReflectionUtil.getDeclaredMethod(KeyboardFocusManager.class, "getNativeFocusOwner");
748             if (getNativeFocusOwner != null) {
749               try {
750                 Object owner = getNativeFocusOwner.invoke(mgr);
751                 if (owner instanceof Component) {
752                   showingWindow = UIUtil.getWindow((Component)owner);
753                 }
754               }
755               catch (Exception e1) {
756                 LOG.debug(e1);
757               }
758             }
759           }
760           if (showingWindow != null) {
761             final IdeFocusManager fm = IdeFocusManager.findInstanceByComponent(showingWindow);
762             ExpirableRunnable maybeRequestDefaultFocus = new ExpirableRunnable() {
763               @Override
764               public void run() {
765                 if (getPopupManager().requestDefaultFocus(false)) return;
766
767                 final Application app = ApplicationManager.getApplication();
768                 if (app != null && app.isActive()) {
769                   fm.requestDefaultFocus(false);
770                 }
771               }
772
773               @Override
774               public boolean isExpired() {
775                 return !UIUtil.isMeaninglessFocusOwner(mgr.getFocusOwner());
776               }
777             };
778             fm.revalidateFocus(maybeRequestDefaultFocus);
779           }
780         }
781       }
782     }
783   }
784
785   public static boolean isMouseEventAhead(@Nullable AWTEvent e) {
786     IdeEventQueue queue = getInstance();
787     return e instanceof MouseEvent ||
788            queue.peekEvent(MouseEvent.MOUSE_PRESSED) != null ||
789            queue.peekEvent(MouseEvent.MOUSE_RELEASED) != null ||
790            queue.peekEvent(MouseEvent.MOUSE_CLICKED) != null;
791   }
792
793   private void enterSuspendModeIfNeeded(AWTEvent e) {
794     if (e instanceof KeyEvent) {
795       if (!mySuspendMode && shallEnterSuspendMode()) {
796         enterSuspendMode();
797       }
798     }
799   }
800
801   private boolean shallEnterSuspendMode() {
802     return peekEvent(WindowEvent.WINDOW_OPENED) != null;
803   }
804
805   private static boolean processAppActivationEvents(AWTEvent e) {
806
807     if (e instanceof WindowEvent) {
808       final WindowEvent we = (WindowEvent)e;
809
810       ApplicationActivationStateManager.get().updateState(we);
811
812       storeLastFocusedComponent(we);
813     }
814
815     return false;
816   }
817
818   private static void storeLastFocusedComponent(WindowEvent we) {
819     final Window eventWindow = we.getWindow();
820
821     if (we.getID() == WindowEvent.WINDOW_DEACTIVATED || we.getID() == WindowEvent.WINDOW_LOST_FOCUS) {
822       Component frame = UIUtil.findUltimateParent(eventWindow);
823       Component focusOwnerInDeactivatedWindow = eventWindow.getMostRecentFocusOwner();
824       IdeFrame[] allProjectFrames = WindowManager.getInstance().getAllProjectFrames();
825
826       if (focusOwnerInDeactivatedWindow != null) {
827         for (IdeFrame ideFrame : allProjectFrames) {
828           JFrame aFrame = WindowManager.getInstance().getFrame(ideFrame.getProject());
829           if (aFrame.equals(frame)) {
830             IdeFocusManager focusManager = IdeFocusManager.getGlobalInstance();
831             if (focusManager instanceof FocusManagerImpl) {
832               ((FocusManagerImpl)focusManager).setLastFocusedAtDeactivation(ideFrame, focusOwnerInDeactivatedWindow);
833             }
834           }
835         }
836       }
837     }
838   }
839
840   private void defaultDispatchEvent(final AWTEvent e) {
841     try {
842       myDispatchingFocusEvent = e instanceof FocusEvent;
843
844       maybeReady();
845       fixStickyAlt(e);
846
847       super.dispatchEvent(e);
848     }
849     catch (Throwable t) {
850       processException(t);
851     }
852     finally {
853       myDispatchingFocusEvent = false;
854     }
855   }
856
857   private static Field ourStickyAltField;
858
859   private static void fixStickyAlt(AWTEvent e) {
860     if (Registry.is("actionSystem.win.suppressAlt.new")) {
861       if (UIUtil.isUnderWindowsLookAndFeel() &&
862           e instanceof InputEvent &&
863           (((InputEvent)e).getModifiers() & (InputEvent.ALT_MASK | InputEvent.ALT_DOWN_MASK)) != 0 &&
864           !(e instanceof KeyEvent && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_ALT)) {
865         try {
866           if (ourStickyAltField == null) {
867             Class<?> aClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsRootPaneUI$AltProcessor");
868             ourStickyAltField = ReflectionUtil.getDeclaredField(aClass, "menuCanceledOnPress");
869           }
870           if (ourStickyAltField != null) {
871             ourStickyAltField.set(null, true);
872           }
873         }
874         catch (Exception exception) {
875           LOG.error(exception);
876         }
877       }
878     }
879     else if (SystemInfo.isWinXpOrNewer && !SystemInfo.isWinVistaOrNewer && e instanceof KeyEvent && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_ALT) {
880       ((KeyEvent)e).consume();  // IDEA-17359
881     }
882   }
883
884   public boolean isDispatchingFocusEvent() {
885     return myDispatchingFocusEvent;
886   }
887
888   private static boolean typeAheadDispatchToFocusManager(AWTEvent e) {
889     if (e instanceof KeyEvent) {
890       final KeyEvent event = (KeyEvent)e;
891       if (!event.isConsumed()) {
892         final IdeFocusManager focusManager = IdeFocusManager.findInstanceByComponent(event.getComponent());
893         return focusManager.dispatch(event);
894       }
895     }
896
897     return false;
898   }
899
900   public void flushQueue() {
901     while (true) {
902       AWTEvent event = peekEvent();
903       if (event == null) return;
904       try {
905         AWTEvent event1 = getNextEvent();
906         dispatchEvent(event1);
907       }
908       catch (Exception e) {
909         LOG.error(e); //?
910       }
911     }
912   }
913
914   public void pumpEventsForHierarchy(Component modalComponent, Condition<AWTEvent> exitCondition) {
915     AWTEvent event;
916     do {
917       try {
918         event = getNextEvent();
919         boolean eventOk = true;
920         if (event instanceof InputEvent) {
921           final Object s = event.getSource();
922           if (s instanceof Component) {
923             Component c = (Component)s;
924             Window modalWindow = modalComponent == null ? null : SwingUtilities.windowForComponent(modalComponent);
925             while (c != null && c != modalWindow) c = c.getParent();
926             if (c == null) {
927               eventOk = false;
928               ((InputEvent)event).consume();
929             }
930           }
931         }
932
933         if (eventOk) {
934           dispatchEvent(event);
935         }
936       }
937       catch (Throwable e) {
938         LOG.error(e);
939         event = null;
940       }
941     }
942     while (!exitCondition.value(event));
943   }
944
945
946   public interface EventDispatcher {
947     boolean dispatch(AWTEvent e);
948   }
949
950
951   private final class MyFireIdleRequest implements Runnable {
952     private final Runnable myRunnable;
953     private final int myTimeout;
954
955
956     public MyFireIdleRequest(@NotNull Runnable runnable, final int timeout) {
957       myTimeout = timeout;
958       myRunnable = runnable;
959     }
960
961
962     @Override
963     public void run() {
964       myRunnable.run();
965       synchronized (myLock) {
966         if (myIdleListeners.contains(myRunnable)) // do not reschedule if not interested anymore
967         {
968           myIdleRequestsAlarm.addRequest(this, myTimeout, ModalityState.NON_MODAL);
969         }
970       }
971     }
972
973     public int getTimeout() {
974       return myTimeout;
975     }
976
977     @Override
978     public String toString() {
979       return "Fire idle request. delay: "+getTimeout()+"; runnable: "+myRunnable;
980     }
981   }
982
983   private final class ExitSuspendModeRunnable implements Runnable {
984
985     @Override
986     public void run() {
987       if (mySuspendMode) {
988         exitSuspendMode();
989       }
990     }
991   }
992
993
994   public long getIdleTime() {
995     return myIdleTime;
996   }
997
998
999   public IdePopupManager getPopupManager() {
1000     return myPopupManager;
1001   }
1002
1003   public IdeKeyEventDispatcher getKeyEventDispatcher() {
1004     return myKeyEventDispatcher;
1005   }
1006
1007   /**
1008    * Same as {@link #blockNextEvents(MouseEvent, IdeEventQueue.BlockMode)} with <code>blockMode</code> equal to <code>COMPLETE</code>.
1009    */
1010   public void blockNextEvents(final MouseEvent e) {
1011     blockNextEvents(e, BlockMode.COMPLETE);
1012   }
1013
1014   /**
1015    * When <code>blockMode</code> is <code>COMPLETE</code>, blocks following related mouse events completely, when <code>blockMode</code> is
1016    * <code>ACTIONS</code> only blocks performing actions bound to corresponding mouse shortcuts.
1017    */
1018   public void blockNextEvents(final MouseEvent e, BlockMode blockMode) {
1019     myMouseEventDispatcher.blockNextEvents(e, blockMode);
1020   }
1021
1022   public boolean isSuspendMode() {
1023     return mySuspendMode;
1024   }
1025
1026   public boolean hasFocusEventsPending() {
1027     return peekEvent(FocusEvent.FOCUS_GAINED) != null || peekEvent(FocusEvent.FOCUS_LOST) != null;
1028   }
1029
1030   private boolean isReady() {
1031     return !myKeyboardBusy && myKeyEventDispatcher.isReady();
1032   }
1033
1034   public void maybeReady() {
1035     flushReady();
1036   }
1037
1038   private void flushReady() {
1039     if (myReady.isEmpty() || !isReady()) return;
1040
1041     Runnable[] ready = myReady.toArray(new Runnable[myReady.size()]);
1042     myReady.clear();
1043
1044     for (Runnable each : ready) {
1045       each.run();
1046     }
1047   }
1048
1049   public void doWhenReady(final Runnable runnable) {
1050     if (EventQueue.isDispatchThread()) {
1051       myReady.add(runnable);
1052       maybeReady();
1053     }
1054     else {
1055       //noinspection SSBasedInspection
1056       SwingUtilities.invokeLater(new Runnable() {
1057         @Override
1058         public void run() {
1059           myReady.add(runnable);
1060           maybeReady();
1061         }
1062       });
1063     }
1064   }
1065
1066   public boolean isPopupActive() {
1067     return myPopupManager.isPopupActive();
1068   }
1069
1070   private static class WindowsAltSuppressor implements EventDispatcher {
1071     private boolean myWaitingForAltRelease;
1072     private Robot myRobot;
1073
1074     @Override
1075     public boolean dispatch(AWTEvent e) {
1076       boolean dispatch = true;
1077       if (e instanceof KeyEvent) {
1078         KeyEvent ke = (KeyEvent)e;
1079         final Component component = ke.getComponent();
1080         boolean pureAlt = ke.getKeyCode() == KeyEvent.VK_ALT && (ke.getModifiers() | InputEvent.ALT_MASK) == InputEvent.ALT_MASK;
1081         if (!pureAlt) {
1082           myWaitingForAltRelease = false;
1083         }
1084         else {
1085           if (ApplicationManager.getApplication() == null ||
1086               UISettings.getInstance() == null ||
1087               !SystemInfo.isWindows ||
1088               !Registry.is("actionSystem.win.suppressAlt") ||
1089               !(UISettings.getInstance().HIDE_TOOL_STRIPES || UISettings.getInstance().PRESENTATION_MODE)) {
1090             return false;
1091           }
1092
1093           if (ke.getID() == KeyEvent.KEY_PRESSED) {
1094             dispatch = !myWaitingForAltRelease;
1095           }
1096           else if (ke.getID() == KeyEvent.KEY_RELEASED) {
1097             if (myWaitingForAltRelease) {
1098               myWaitingForAltRelease = false;
1099               dispatch = false;
1100             }
1101             else if (component != null) {
1102               //noinspection SSBasedInspection
1103               SwingUtilities.invokeLater(new Runnable() {
1104                 @Override
1105                 public void run() {
1106                   try {
1107                     final Window window = UIUtil.getWindow(component);
1108                     if (window == null || !window.isActive()) {
1109                       return;
1110                     }
1111                     myWaitingForAltRelease = true;
1112                     if (myRobot == null) {
1113                       myRobot = new Robot();
1114                     }
1115                     myRobot.keyPress(KeyEvent.VK_ALT);
1116                     myRobot.keyRelease(KeyEvent.VK_ALT);
1117                   }
1118                   catch (AWTException e1) {
1119                     LOG.debug(e1);
1120                   }
1121                 }
1122               });
1123             }
1124           }
1125         }
1126       }
1127
1128       return !dispatch;
1129     }
1130   }
1131
1132   public boolean isInputMethodEnabled() {
1133     return !SystemInfo.isMac || myInputMethodLock == 0;
1134   }
1135
1136   public void disableInputMethods(Disposable parentDisposable) {
1137     myInputMethodLock++;
1138     Disposer.register(parentDisposable, new Disposable() {
1139       @Override
1140       public void dispose() {
1141         myInputMethodLock--;
1142       }
1143     });
1144   }
1145
1146   private final FrequentEventDetector myFrequentEventDetector = new FrequentEventDetector(1009, 100);
1147   @Override
1148   public void postEvent(@NotNull AWTEvent theEvent) {
1149     myFrequentEventDetector.eventHappened();
1150     super.postEvent(theEvent);
1151   }
1152
1153   @Override
1154   public AWTEvent peekEvent() {
1155     AWTEvent event = super.peekEvent();
1156     if (event != null) {
1157       return event;
1158     }
1159     if (isTestMode() && LaterInvocator.ensureFlushRequested()) {
1160       return super.peekEvent();
1161     }
1162     return null;
1163   }
1164
1165   private Boolean myTestMode;
1166   private boolean isTestMode() {
1167     Boolean testMode = myTestMode;
1168     if (testMode != null) return testMode;
1169     
1170     Application application = ApplicationManager.getApplication();
1171     if (application == null) return false;
1172
1173     testMode = application.isUnitTestMode();
1174     myTestMode = testMode;
1175     return testMode;
1176   }
1177
1178   /**
1179    * @see IdeEventQueue#blockNextEvents(MouseEvent, IdeEventQueue.BlockMode)
1180    */
1181   public enum BlockMode {
1182     COMPLETE, ACTIONS
1183   }
1184 }