2 * Copyright 2000-2015 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.ide;
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;
55 import javax.swing.plaf.basic.ComboPopup;
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;
68 * @author Vladimir Kondratyev
69 * @author Anton Katilin
71 public class IdeEventQueue extends EventQueue {
72 private static final Logger LOG = Logger.getInstance("#com.intellij.ide.IdeEventQueue");
75 * Adding/Removing of "idle" listeners should be thread safe.
77 private final Object myLock = new Object();
79 private final List<Runnable> myIdleListeners = ContainerUtil.createLockFreeCopyOnWriteList();
81 private final List<Runnable> myActivityListeners = ContainerUtil.createLockFreeCopyOnWriteList();
83 private final Alarm myIdleRequestsAlarm = new Alarm();
85 private final Alarm myIdleTimeCounterAlarm = new Alarm();
87 private long myIdleTime;
89 private final Map<Runnable, MyFireIdleRequest> myListener2Request = new HashMap<Runnable, MyFireIdleRequest>();
90 // IdleListener -> MyFireIdleRequest
92 private final IdeKeyEventDispatcher myKeyEventDispatcher = new IdeKeyEventDispatcher(this);
94 private final IdeMouseEventDispatcher myMouseEventDispatcher = new IdeMouseEventDispatcher();
96 private final IdePopupManager myPopupManager = new IdePopupManager();
99 private final ToolkitBugsProcessor myToolkitBugsProcessor = new ToolkitBugsProcessor();
101 private boolean mySuspendMode;
104 * We exit from suspend mode when focus owner changes and no more WindowEvent.WINDOW_OPENED events
106 * in the queue. If WINDOW_OPENED event does exists in the queues then we restart the alarm.
108 private Component myFocusOwner;
110 private final Runnable myExitSuspendModeRunnable = new ExitSuspendModeRunnable();
113 * We exit from suspend mode when this alarm is triggered and no mode WindowEvent.WINDOW_OPENED
115 * events in the queue. If WINDOW_OPENED event does exist then we restart the alarm.
117 private final Alarm mySuspendModeAlarm = new Alarm();
120 * Counter of processed events. It is used to assert that data context lives only inside single
124 private int myEventCount;
126 private boolean myIsInInputEvent;
128 private AWTEvent myCurrentEvent;
130 private long myLastActiveTime;
132 private WindowManagerEx myWindowManager;
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();
138 private boolean myKeyboardBusy;
139 private boolean myDispatchingFocusEvent;
141 private int myInputMethodLock;
143 private static class IdeEventQueueHolder {
144 private static final IdeEventQueue INSTANCE = new IdeEventQueue();
147 public static IdeEventQueue getInstance() {
148 return IdeEventQueueHolder.INSTANCE;
151 private IdeEventQueue() {
152 EventQueue systemEventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
153 assert !(systemEventQueue instanceof IdeEventQueue) : systemEventQueue;
154 systemEventQueue.push(this);
155 addIdleTimeCounterRequest();
157 final KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
158 keyboardFocusManager.addPropertyChangeListener("permanentFocusOwner", new PropertyChangeListener() {
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
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)) {
176 addDispatcher(new WindowsAltSuppressor(), null);
180 public void setWindowManager(final WindowManagerEx windowManager) {
181 myWindowManager = windowManager;
185 private void addIdleTimeCounterRequest() {
186 if (isTestMode()) return;
188 myIdleTimeCounterAlarm.cancelAllRequests();
189 myLastActiveTime = System.currentTimeMillis();
190 myIdleTimeCounterAlarm.addRequest(new Runnable() {
193 myIdleTime += System.currentTimeMillis() - myLastActiveTime;
194 addIdleTimeCounterRequest();
196 }, 20000, ModalityState.NON_MODAL);
200 * This class performs special processing in order to have {@link #getIdleTime()} return more or less up-to-date data.
202 * This method allows to stop that processing (convenient in non-intellij environment like upsource).
204 @SuppressWarnings("unused") // Used in upsource.
205 public void stopIdleTimeCalculation() {
206 myIdleTimeCounterAlarm.cancelAllRequests();
209 public boolean shouldNotTypeInEditor() {
210 return myKeyEventDispatcher.isWaitingForSecondKeyStroke() || mySuspendMode;
214 private void enterSuspendMode() {
215 mySuspendMode = true;
216 myFocusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
217 mySuspendModeAlarm.cancelAllRequests();
218 mySuspendModeAlarm.addRequest(myExitSuspendModeRunnable, 750);
222 * Exits suspend mode and pumps all suspended events.
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);
233 // Now we can pump all suspended events.
234 mySuspendMode = false;
235 myFocusOwner = null; // to prevent memory leaks
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);
250 public void removeIdleListener(@NotNull final Runnable runnable) {
251 synchronized (myLock) {
252 final boolean wasRemoved = myIdleListeners.remove(runnable);
254 LOG.error("unknown runnable: " + runnable);
256 final MyFireIdleRequest request = myListener2Request.remove(runnable);
257 LOG.assertTrue(request != null);
258 myIdleRequestsAlarm.cancelRequest(request);
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);
270 public void addActivityListener(@NotNull final Runnable runnable, Disposable parentDisposable) {
271 synchronized (myLock) {
272 ContainerUtil.add(runnable, myActivityListeners, parentDisposable);
276 public void removeActivityListener(@NotNull final Runnable runnable) {
277 synchronized (myLock) {
278 myActivityListeners.remove(runnable);
283 public void addDispatcher(final EventDispatcher dispatcher, Disposable parent) {
284 _addProcessor(dispatcher, parent, myDispatchers);
287 public void removeDispatcher(EventDispatcher dispatcher) {
288 myDispatchers.remove(dispatcher);
291 public boolean containsDispatcher(EventDispatcher dispatcher) {
292 return myDispatchers.contains(dispatcher);
295 public void addPostprocessor(EventDispatcher dispatcher, @Nullable Disposable parent) {
296 _addProcessor(dispatcher, parent, myPostProcessors);
299 public void removePostprocessor(EventDispatcher dispatcher) {
300 myPostProcessors.remove(dispatcher);
303 private static void _addProcessor(final EventDispatcher dispatcher, Disposable parent, final Set<EventDispatcher> set) {
305 if (parent != null) {
306 Disposer.register(parent, new Disposable() {
308 public void dispose() {
309 set.remove(dispatcher);
315 public int getEventCount() {
319 public void setEventCount(int evCount) {
320 myEventCount = evCount;
323 public AWTEvent getTrueCurrentEvent() {
324 return myCurrentEvent;
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;
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());
338 lastMouseWheel = System.currentTimeMillis();
340 int modifiers = mwe.getModifiers() | mwe.getModifiersEx();
341 return MouseEventAdapter.convert(mwe, wheelDestinationComponent, mwe.getID(), lastMouseWheel, modifiers, mwe.getX(), mwe.getY());
347 private static boolean ourAppIsLoaded;
349 private static boolean appIsLoaded() {
350 if (ourAppIsLoaded) return true;
351 boolean loaded = IdeaApplication.isLoaded();
352 if (loaded) ourAppIsLoaded = true;
357 public void dispatchEvent(@NotNull AWTEvent e) {
358 if (!appIsLoaded()) {
360 super.dispatchEvent(e);
362 catch (Throwable t) {
368 e = InertialMouseRouter.changeSourceIfNeeded(e);
370 e = fixNonEnglishKeyboardLayouts(e);
374 boolean wasInputEvent = myIsInInputEvent;
375 myIsInInputEvent = e instanceof InputEvent || e instanceof InputMethodEvent || e instanceof WindowEvent || e instanceof ActionEvent;
376 AWTEvent oldEvent = myCurrentEvent;
380 _dispatchEvent(e, false);
382 catch (Throwable t) {
386 myIsInInputEvent = wasInputEvent;
387 myCurrentEvent = oldEvent;
389 for (EventDispatcher each : myPostProcessors) {
393 if (e instanceof KeyEvent) {
399 private void processException(Throwable t) {
400 if (!myToolkitBugsProcessor.process(t)) {
401 PluginManager.processException(t);
405 private static int ctrlIsPressedCount;
406 private static boolean leftAltIsPressed;
407 //private static boolean altGrIsPressed = false;
409 private static AWTEvent fixNonEnglishKeyboardLayouts(AWTEvent e) {
410 if (!(e instanceof KeyEvent)) return e;
412 KeyboardSettingsExternalizable externalizable = KeyboardSettingsExternalizable.getInstance();
413 if (externalizable == null || !externalizable.isNonEnglishKeyboardSupportEnabled()) return e;
415 KeyEvent ke = (KeyEvent)e;
417 // Try to get it from editor
418 Component sourceComponent = WindowManagerEx.getInstanceEx().getMostRecentFocusedWindow();
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++;
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;
436 else if (ke.getID() == KeyEvent.KEY_RELEASED) {
437 switch (ke.getKeyCode()) {
438 case KeyEvent.VK_CONTROL:
439 ctrlIsPressedCount--;
441 case KeyEvent.VK_ALT:
442 if (ke.getKeyLocation() == KeyEvent.KEY_LOCATION_LEFT) {
443 leftAltIsPressed = false;
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);
459 Integer keyCodeFromChar = CharToVKeyMap.get(ke.getKeyChar());
460 if (keyCodeFromChar != null) {
461 if (keyCodeFromChar != ke.getKeyCode()) {
462 // non-english layout
463 ke.setKeyCode(keyCodeFromChar);
466 //for (int i = 0; sourceComponent == null && i < WindowManagerEx.getInstanceEx().getAllProjectFrames().length; i++) {
467 // sourceComponent = WindowManagerEx.getInstanceEx().getAllProjectFrames()[i].getComponent();
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
476 int modifiers = ke.getModifiersEx() ^ InputEvent.ALT_DOWN_MASK ^ InputEvent.CTRL_DOWN_MASK;
478 if (ctrlIsPressedCount > 1) {
479 modifiers |= InputEvent.CTRL_DOWN_MASK;
482 if (leftAltIsPressed) {
483 modifiers |= InputEvent.ALT_MASK;
486 int oldKeyCode = ke.getKeyCode();
488 //noinspection MagicConstant
489 ke = new KeyEvent(ke.getComponent(), ke.getID(), ke.getWhen(), modifiers,
490 KeyEvent.VK_UNDEFINED, ke.getKeyChar(), KeyEvent.KEY_LOCATION_UNKNOWN);
492 ke.setKeyCode(oldKeyCode);
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);
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);
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);
531 traceClipboardEvents(e);
533 if (processAppActivationEvents(e)) return;
535 if (!typeAheadFlushing) {
536 fixStickyFocusedComponents(e);
539 if (!myPopupManager.isPopupActive()) {
540 enterSuspendModeIfNeeded(e);
543 myKeyboardBusy = e instanceof KeyEvent ||
544 peekEvent(KeyEvent.KEY_PRESSED) != null ||
545 peekEvent(KeyEvent.KEY_RELEASED) != null ||
546 peekEvent(KeyEvent.KEY_TYPED) != null;
548 if (e instanceof KeyEvent) {
549 if (e.getID() == KeyEvent.KEY_RELEASED && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_SHIFT) {
550 myMouseEventDispatcher.resetHorScrollingTracker();
554 if (!typeAheadFlushing && typeAheadDispatchToFocusManager(e)) {
558 if (e instanceof WindowEvent) {
559 ActivityTracker.getInstance().inc();
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();
571 // Process "idle" and "activity" listeners
572 if (e instanceof KeyEvent || e instanceof MouseEvent) {
573 ActivityTracker.getInstance().inc();
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);
583 myIdleRequestsAlarm.addRequest(request, request.getTimeout(), ModalityState.NON_MODAL);
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();
598 if (myPopupManager.isPopupActive() && myPopupManager.dispatch(e)) {
599 if (myKeyEventDispatcher.isWaitingForSecondKeyStroke()) {
600 myKeyEventDispatcher.setState(KeyState.STATE_INIT);
606 for (EventDispatcher eachDispatcher : myDispatchers) {
607 if (eachDispatcher.dispatch(e)) {
612 if (e instanceof InputMethodEvent) {
613 if (SystemInfo.isMac && myKeyEventDispatcher.isWaitingForSecondKeyStroke()) {
617 if (e instanceof ComponentEvent && myWindowManager != null) {
618 myWindowManager.dispatchComponentEvent((ComponentEvent)e);
620 if (e instanceof KeyEvent) {
621 if (mySuspendMode || !myKeyEventDispatcher.dispatchKeyEvent((KeyEvent)e)) {
622 defaultDispatchEvent(e);
625 ((KeyEvent)e).consume();
626 defaultDispatchEvent(e);
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() {
639 dispatchEvent(toDispatch);
643 if (!myMouseEventDispatcher.dispatchMouseEvent(me)) {
644 defaultDispatchEvent(e);
648 defaultDispatchEvent(e);
652 private static final Field ourInvocationEventRunnableAccessor;
656 if (Registry.is("trace.clipboard.events") && SystemInfo.isJavaVersionAtLeast("1.8.0_60")) {
658 field = InvocationEvent.class.getDeclaredField("runnable");
659 field.setAccessible(true);
661 catch (Exception e) {
662 LOG.warn("Error creating accessor for java.awt.event.InvocationEvent.runnable field", e);
665 ourInvocationEventRunnableAccessor = field;
668 private static void traceClipboardEvents(AWTEvent e) {
669 if (ourInvocationEventRunnableAccessor != null && e instanceof InvocationEvent && e.getClass().getName().equals("sun.awt.PeerEvent")) {
671 Object r = ourInvocationEventRunnableAccessor.get(e);
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");
680 catch (Exception ex) {
681 LOG.warn("Error accessing java.awt.event.InvocationEvent.runnable field");
686 private static void fixStickyWindow(KeyboardFocusManager mgr, Window wnd, String resetMethod) {
687 Window showingWindow = wnd;
689 if (wnd != null && !wnd.isShowing()) {
690 while (showingWindow != null) {
691 if (showingWindow.isShowing()) break;
692 showingWindow = (Window)showingWindow.getParent();
695 if (showingWindow == null) {
696 final Frame[] allFrames = Frame.getFrames();
697 for (Frame each : allFrames) {
698 if (each.isShowing()) {
699 showingWindow = each;
706 if (showingWindow != null && showingWindow != wnd) {
707 final Method setActive = ReflectionUtil.findMethod(ReflectionUtil.getClassDeclaredMethods(KeyboardFocusManager.class, false), resetMethod, Window.class);
708 if (setActive != null) {
710 setActive.invoke(mgr, (Window)showingWindow);
712 catch (Exception exc) {
720 public void fixStickyFocusedComponents(@Nullable AWTEvent e) {
721 if (e != null && !(e instanceof InputEvent)) return;
723 final KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
725 if (Registry.is("actionSystem.fixStickyFocusedWindows")) {
726 fixStickyWindow(mgr, mgr.getActiveWindow(), "setGlobalActiveWindow");
727 fixStickyWindow(mgr, mgr.getFocusedWindow(), "setGlobalFocusedWindow");
730 if (Registry.is("actionSystem.fixNullFocusedComponent")) {
731 final Component focusOwner = mgr.getFocusOwner();
732 if (focusOwner == null || !focusOwner.isShowing() || focusOwner instanceof JFrame || focusOwner instanceof JDialog) {
734 final Application app = ApplicationManager.getApplication();
735 if (app instanceof ApplicationEx && !((ApplicationEx) app).isLoaded()) {
739 boolean mouseEventsAhead = isMouseEventAhead(e);
740 boolean focusTransferredNow = IdeFocusManager.getGlobalInstance().isFocusBeingTransferred();
742 boolean okToFixFocus = !mouseEventsAhead && !focusTransferredNow;
745 Window showingWindow = mgr.getActiveWindow();
746 if (showingWindow == null) {
747 Method getNativeFocusOwner = ReflectionUtil.getDeclaredMethod(KeyboardFocusManager.class, "getNativeFocusOwner");
748 if (getNativeFocusOwner != null) {
750 Object owner = getNativeFocusOwner.invoke(mgr);
751 if (owner instanceof Component) {
752 showingWindow = UIUtil.getWindow((Component)owner);
755 catch (Exception e1) {
760 if (showingWindow != null) {
761 final IdeFocusManager fm = IdeFocusManager.findInstanceByComponent(showingWindow);
762 ExpirableRunnable maybeRequestDefaultFocus = new ExpirableRunnable() {
765 if (getPopupManager().requestDefaultFocus(false)) return;
767 final Application app = ApplicationManager.getApplication();
768 if (app != null && app.isActive()) {
769 fm.requestDefaultFocus(false);
774 public boolean isExpired() {
775 return !UIUtil.isMeaninglessFocusOwner(mgr.getFocusOwner());
778 fm.revalidateFocus(maybeRequestDefaultFocus);
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;
793 private void enterSuspendModeIfNeeded(AWTEvent e) {
794 if (e instanceof KeyEvent) {
795 if (!mySuspendMode && shallEnterSuspendMode()) {
801 private boolean shallEnterSuspendMode() {
802 return peekEvent(WindowEvent.WINDOW_OPENED) != null;
805 private static boolean processAppActivationEvents(AWTEvent e) {
807 if (e instanceof WindowEvent) {
808 final WindowEvent we = (WindowEvent)e;
810 ApplicationActivationStateManager.get().updateState(we);
812 storeLastFocusedComponent(we);
818 private static void storeLastFocusedComponent(WindowEvent we) {
819 final Window eventWindow = we.getWindow();
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();
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);
840 private void defaultDispatchEvent(final AWTEvent e) {
842 myDispatchingFocusEvent = e instanceof FocusEvent;
847 super.dispatchEvent(e);
849 catch (Throwable t) {
853 myDispatchingFocusEvent = false;
857 private static Field ourStickyAltField;
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)) {
866 if (ourStickyAltField == null) {
867 Class<?> aClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsRootPaneUI$AltProcessor");
868 ourStickyAltField = ReflectionUtil.getDeclaredField(aClass, "menuCanceledOnPress");
870 if (ourStickyAltField != null) {
871 ourStickyAltField.set(null, true);
874 catch (Exception exception) {
875 LOG.error(exception);
879 else if (SystemInfo.isWinXpOrNewer && !SystemInfo.isWinVistaOrNewer && e instanceof KeyEvent && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_ALT) {
880 ((KeyEvent)e).consume(); // IDEA-17359
884 public boolean isDispatchingFocusEvent() {
885 return myDispatchingFocusEvent;
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);
900 public void flushQueue() {
902 AWTEvent event = peekEvent();
903 if (event == null) return;
905 AWTEvent event1 = getNextEvent();
906 dispatchEvent(event1);
908 catch (Exception e) {
914 public void pumpEventsForHierarchy(Component modalComponent, Condition<AWTEvent> exitCondition) {
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();
928 ((InputEvent)event).consume();
934 dispatchEvent(event);
937 catch (Throwable e) {
942 while (!exitCondition.value(event));
946 public interface EventDispatcher {
947 boolean dispatch(AWTEvent e);
951 private final class MyFireIdleRequest implements Runnable {
952 private final Runnable myRunnable;
953 private final int myTimeout;
956 public MyFireIdleRequest(@NotNull Runnable runnable, final int timeout) {
958 myRunnable = runnable;
965 synchronized (myLock) {
966 if (myIdleListeners.contains(myRunnable)) // do not reschedule if not interested anymore
968 myIdleRequestsAlarm.addRequest(this, myTimeout, ModalityState.NON_MODAL);
973 public int getTimeout() {
978 public String toString() {
979 return "Fire idle request. delay: "+getTimeout()+"; runnable: "+myRunnable;
983 private final class ExitSuspendModeRunnable implements Runnable {
994 public long getIdleTime() {
999 public IdePopupManager getPopupManager() {
1000 return myPopupManager;
1003 public IdeKeyEventDispatcher getKeyEventDispatcher() {
1004 return myKeyEventDispatcher;
1008 * Same as {@link #blockNextEvents(MouseEvent, IdeEventQueue.BlockMode)} with <code>blockMode</code> equal to <code>COMPLETE</code>.
1010 public void blockNextEvents(final MouseEvent e) {
1011 blockNextEvents(e, BlockMode.COMPLETE);
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.
1018 public void blockNextEvents(final MouseEvent e, BlockMode blockMode) {
1019 myMouseEventDispatcher.blockNextEvents(e, blockMode);
1022 public boolean isSuspendMode() {
1023 return mySuspendMode;
1026 public boolean hasFocusEventsPending() {
1027 return peekEvent(FocusEvent.FOCUS_GAINED) != null || peekEvent(FocusEvent.FOCUS_LOST) != null;
1030 private boolean isReady() {
1031 return !myKeyboardBusy && myKeyEventDispatcher.isReady();
1034 public void maybeReady() {
1038 private void flushReady() {
1039 if (myReady.isEmpty() || !isReady()) return;
1041 Runnable[] ready = myReady.toArray(new Runnable[myReady.size()]);
1044 for (Runnable each : ready) {
1049 public void doWhenReady(final Runnable runnable) {
1050 if (EventQueue.isDispatchThread()) {
1051 myReady.add(runnable);
1055 //noinspection SSBasedInspection
1056 SwingUtilities.invokeLater(new Runnable() {
1059 myReady.add(runnable);
1066 public boolean isPopupActive() {
1067 return myPopupManager.isPopupActive();
1070 private static class WindowsAltSuppressor implements EventDispatcher {
1071 private boolean myWaitingForAltRelease;
1072 private Robot myRobot;
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;
1082 myWaitingForAltRelease = false;
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)) {
1093 if (ke.getID() == KeyEvent.KEY_PRESSED) {
1094 dispatch = !myWaitingForAltRelease;
1096 else if (ke.getID() == KeyEvent.KEY_RELEASED) {
1097 if (myWaitingForAltRelease) {
1098 myWaitingForAltRelease = false;
1101 else if (component != null) {
1102 //noinspection SSBasedInspection
1103 SwingUtilities.invokeLater(new Runnable() {
1107 final Window window = UIUtil.getWindow(component);
1108 if (window == null || !window.isActive()) {
1111 myWaitingForAltRelease = true;
1112 if (myRobot == null) {
1113 myRobot = new Robot();
1115 myRobot.keyPress(KeyEvent.VK_ALT);
1116 myRobot.keyRelease(KeyEvent.VK_ALT);
1118 catch (AWTException e1) {
1132 public boolean isInputMethodEnabled() {
1133 return !SystemInfo.isMac || myInputMethodLock == 0;
1136 public void disableInputMethods(Disposable parentDisposable) {
1137 myInputMethodLock++;
1138 Disposer.register(parentDisposable, new Disposable() {
1140 public void dispose() {
1141 myInputMethodLock--;
1146 private final FrequentEventDetector myFrequentEventDetector = new FrequentEventDetector(1009, 100);
1148 public void postEvent(@NotNull AWTEvent theEvent) {
1149 myFrequentEventDetector.eventHappened();
1150 super.postEvent(theEvent);
1154 public AWTEvent peekEvent() {
1155 AWTEvent event = super.peekEvent();
1156 if (event != null) {
1159 if (isTestMode() && LaterInvocator.ensureFlushRequested()) {
1160 return super.peekEvent();
1165 private Boolean myTestMode;
1166 private boolean isTestMode() {
1167 Boolean testMode = myTestMode;
1168 if (testMode != null) return testMode;
1170 Application application = ApplicationManager.getApplication();
1171 if (application == null) return false;
1173 testMode = application.isUnitTestMode();
1174 myTestMode = testMode;
1179 * @see IdeEventQueue#blockNextEvents(MouseEvent, IdeEventQueue.BlockMode)
1181 public enum BlockMode {