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 UIUtil.invokeLaterIfNeeded(new Runnable() {
249 myIdleRequestsAlarm.addRequest(request, timeout);
255 public void removeIdleListener(@NotNull final Runnable runnable) {
256 synchronized (myLock) {
257 final boolean wasRemoved = myIdleListeners.remove(runnable);
259 LOG.error("unknown runnable: " + runnable);
261 final MyFireIdleRequest request = myListener2Request.remove(runnable);
262 LOG.assertTrue(request != null);
263 myIdleRequestsAlarm.cancelRequest(request);
267 /** @deprecated use {@link #addActivityListener(Runnable, Disposable)} (to be removed in IDEA 17) */
268 @SuppressWarnings("unused")
269 public void addActivityListener(@NotNull final Runnable runnable) {
270 synchronized (myLock) {
271 myActivityListeners.add(runnable);
275 public void addActivityListener(@NotNull final Runnable runnable, Disposable parentDisposable) {
276 synchronized (myLock) {
277 ContainerUtil.add(runnable, myActivityListeners, parentDisposable);
281 public void removeActivityListener(@NotNull final Runnable runnable) {
282 synchronized (myLock) {
283 myActivityListeners.remove(runnable);
288 public void addDispatcher(final EventDispatcher dispatcher, Disposable parent) {
289 _addProcessor(dispatcher, parent, myDispatchers);
292 public void removeDispatcher(EventDispatcher dispatcher) {
293 myDispatchers.remove(dispatcher);
296 public boolean containsDispatcher(EventDispatcher dispatcher) {
297 return myDispatchers.contains(dispatcher);
300 public void addPostprocessor(EventDispatcher dispatcher, @Nullable Disposable parent) {
301 _addProcessor(dispatcher, parent, myPostProcessors);
304 public void removePostprocessor(EventDispatcher dispatcher) {
305 myPostProcessors.remove(dispatcher);
308 private static void _addProcessor(final EventDispatcher dispatcher, Disposable parent, final Set<EventDispatcher> set) {
310 if (parent != null) {
311 Disposer.register(parent, new Disposable() {
313 public void dispose() {
314 set.remove(dispatcher);
320 public int getEventCount() {
324 public void setEventCount(int evCount) {
325 myEventCount = evCount;
328 public AWTEvent getTrueCurrentEvent() {
329 return myCurrentEvent;
332 private static class InertialMouseRouter {
333 private static final int MOUSE_WHEEL_RESTART_THRESHOLD = 50;
334 private static Component wheelDestinationComponent;
335 private static long lastMouseWheel;
337 private static AWTEvent changeSourceIfNeeded(AWTEvent awtEvent) {
338 if (SystemInfo.isMac && Registry.is("ide.inertial.mouse.fix") && awtEvent instanceof MouseWheelEvent) {
339 MouseWheelEvent mwe = (MouseWheelEvent) awtEvent;
340 if (mwe.getWhen() - lastMouseWheel > MOUSE_WHEEL_RESTART_THRESHOLD) {
341 wheelDestinationComponent = SwingUtilities.getDeepestComponentAt(mwe.getComponent(), mwe.getX(), mwe.getY());
343 lastMouseWheel = System.currentTimeMillis();
345 int modifiers = mwe.getModifiers() | mwe.getModifiersEx();
346 return MouseEventAdapter.convert(mwe, wheelDestinationComponent, mwe.getID(), lastMouseWheel, modifiers, mwe.getX(), mwe.getY());
352 private static boolean ourAppIsLoaded;
354 private static boolean appIsLoaded() {
355 if (ourAppIsLoaded) return true;
356 boolean loaded = IdeaApplication.isLoaded();
357 if (loaded) ourAppIsLoaded = true;
362 public void dispatchEvent(@NotNull AWTEvent e) {
363 if (!appIsLoaded()) {
365 super.dispatchEvent(e);
367 catch (Throwable t) {
373 e = InertialMouseRouter.changeSourceIfNeeded(e);
375 e = fixNonEnglishKeyboardLayouts(e);
379 boolean wasInputEvent = myIsInInputEvent;
380 myIsInInputEvent = e instanceof InputEvent || e instanceof InputMethodEvent || e instanceof WindowEvent || e instanceof ActionEvent;
381 AWTEvent oldEvent = myCurrentEvent;
385 _dispatchEvent(e, false);
387 catch (Throwable t) {
391 myIsInInputEvent = wasInputEvent;
392 myCurrentEvent = oldEvent;
394 for (EventDispatcher each : myPostProcessors) {
398 if (e instanceof KeyEvent) {
404 private void processException(Throwable t) {
405 if (!myToolkitBugsProcessor.process(t)) {
406 PluginManager.processException(t);
410 private static int ctrlIsPressedCount;
411 private static boolean leftAltIsPressed;
412 //private static boolean altGrIsPressed = false;
414 private static AWTEvent fixNonEnglishKeyboardLayouts(AWTEvent e) {
415 if (!(e instanceof KeyEvent)) return e;
417 KeyboardSettingsExternalizable externalizable = KeyboardSettingsExternalizable.getInstance();
418 if (externalizable == null || !externalizable.isNonEnglishKeyboardSupportEnabled()) return e;
420 KeyEvent ke = (KeyEvent)e;
422 // Try to get it from editor
423 Component sourceComponent = WindowManagerEx.getInstanceEx().getMostRecentFocusedWindow();
425 if (ke.getID() == KeyEvent.KEY_PRESSED) {
426 switch (ke.getKeyCode()) {
427 case KeyEvent.VK_CONTROL:
428 if ((ke.getModifiersEx() & (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) != (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) {
429 ctrlIsPressedCount++;
432 case KeyEvent.VK_ALT:
433 if (ke.getKeyLocation() == KeyEvent.KEY_LOCATION_LEFT) {
434 if ((ke.getModifiersEx() & (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) != (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) {
435 leftAltIsPressed = true;
441 else if (ke.getID() == KeyEvent.KEY_RELEASED) {
442 switch (ke.getKeyCode()) {
443 case KeyEvent.VK_CONTROL:
444 ctrlIsPressedCount--;
446 case KeyEvent.VK_ALT:
447 if (ke.getKeyLocation() == KeyEvent.KEY_LOCATION_LEFT) {
448 leftAltIsPressed = false;
454 if (!leftAltIsPressed && KeyboardSettingsExternalizable.getInstance().isUkrainianKeyboard(sourceComponent)) {
455 if ('Ò‘' == ke.getKeyChar() || ke.getKeyCode() == KeyEvent.VK_U) {
456 ke = new KeyEvent(ke.getComponent(), ke.getID(), ke.getWhen(), 0,
457 KeyEvent.VK_UNDEFINED, 'Ò‘', ke.getKeyLocation());
458 ke.setKeyCode(KeyEvent.VK_U);
464 Integer keyCodeFromChar = CharToVKeyMap.get(ke.getKeyChar());
465 if (keyCodeFromChar != null) {
466 if (keyCodeFromChar != ke.getKeyCode()) {
467 // non-english layout
468 ke.setKeyCode(keyCodeFromChar);
471 //for (int i = 0; sourceComponent == null && i < WindowManagerEx.getInstanceEx().getAllProjectFrames().length; i++) {
472 // sourceComponent = WindowManagerEx.getInstanceEx().getAllProjectFrames()[i].getComponent();
475 if (sourceComponent != null) {
476 if (KeyboardSettingsExternalizable.isSupportedKeyboardLayout(sourceComponent)) {
477 if ((ke.getModifiersEx() & (InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) != 0 /*&& ke.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT*/) {
478 // On German keyboard layout on Windows we are getting on key press
479 // ctrl + alt instead of AltGr
481 int modifiers = ke.getModifiersEx() ^ InputEvent.ALT_DOWN_MASK ^ InputEvent.CTRL_DOWN_MASK;
483 if (ctrlIsPressedCount > 1) {
484 modifiers |= InputEvent.CTRL_DOWN_MASK;
487 if (leftAltIsPressed) {
488 modifiers |= InputEvent.ALT_MASK;
491 int oldKeyCode = ke.getKeyCode();
493 //noinspection MagicConstant
494 ke = new KeyEvent(ke.getComponent(), ke.getID(), ke.getWhen(), modifiers,
495 KeyEvent.VK_UNDEFINED, ke.getKeyChar(), KeyEvent.KEY_LOCATION_UNKNOWN);
497 ke.setKeyCode(oldKeyCode);
506 private static AWTEvent mapEvent(AWTEvent e) {
507 if (SystemInfo.isXWindow && e instanceof MouseEvent && ((MouseEvent)e).getButton() > 3) {
508 MouseEvent src = (MouseEvent)e;
509 if (src.getButton() < 6) {
510 // Convert these events(buttons 4&5 in are produced by touchpad, they must be converted to horizontal scrolling events
511 e = new MouseWheelEvent(src.getComponent(), src.getID(), src.getWhen(),
512 src.getModifiers() | InputEvent.SHIFT_DOWN_MASK, src.getX(), src.getY(),
513 0, false, MouseWheelEvent.WHEEL_UNIT_SCROLL, src.getClickCount(), src.getButton() == 4 ? -1 : 1);
516 // Here we "shift" events with buttons 6 and 7 to similar events with buttons 4 and 5
517 // See java.awt.InputEvent#BUTTON_DOWN_MASK, 1<<14 is 4th physical button, 1<<15 is 5th.
518 //noinspection MagicConstant
519 e = new MouseEvent(src.getComponent(), src.getID(), src.getWhen(), src.getModifiers() | (1 << 8 + src.getButton()),
520 src.getX(), src.getY(), 1, src.isPopupTrigger(), src.getButton() - 2);
526 public void _dispatchEvent(@NotNull AWTEvent e, boolean typeAheadFlushing) {
527 if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
528 DnDManagerImpl dndManager = (DnDManagerImpl)DnDManager.getInstance();
529 if (dndManager != null) {
530 dndManager.setLastDropHandler(null);
536 traceClipboardEvents(e);
538 if (processAppActivationEvents(e)) return;
540 if (!typeAheadFlushing) {
541 fixStickyFocusedComponents(e);
544 if (!myPopupManager.isPopupActive()) {
545 enterSuspendModeIfNeeded(e);
548 myKeyboardBusy = e instanceof KeyEvent ||
549 peekEvent(KeyEvent.KEY_PRESSED) != null ||
550 peekEvent(KeyEvent.KEY_RELEASED) != null ||
551 peekEvent(KeyEvent.KEY_TYPED) != null;
553 if (e instanceof KeyEvent) {
554 if (e.getID() == KeyEvent.KEY_RELEASED && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_SHIFT) {
555 myMouseEventDispatcher.resetHorScrollingTracker();
559 if (!typeAheadFlushing && typeAheadDispatchToFocusManager(e)) {
563 if (e instanceof WindowEvent) {
564 ActivityTracker.getInstance().inc();
567 if (e instanceof MouseWheelEvent) {
568 final MenuElement[] selectedPath = MenuSelectionManager.defaultManager().getSelectedPath();
569 if (selectedPath.length > 0 && !(selectedPath[0] instanceof ComboPopup)) {
570 ((MouseWheelEvent)e).consume();
576 // Process "idle" and "activity" listeners
577 if (e instanceof KeyEvent || e instanceof MouseEvent) {
578 ActivityTracker.getInstance().inc();
580 synchronized (myLock) {
581 myIdleRequestsAlarm.cancelAllRequests();
582 for (Runnable idleListener : myIdleListeners) {
583 final MyFireIdleRequest request = myListener2Request.get(idleListener);
584 if (request == null) {
585 LOG.error("There is no request for " + idleListener);
588 myIdleRequestsAlarm.addRequest(request, request.getTimeout(), ModalityState.NON_MODAL);
591 if (KeyEvent.KEY_PRESSED == e.getID() ||
592 KeyEvent.KEY_TYPED == e.getID() ||
593 MouseEvent.MOUSE_PRESSED == e.getID() ||
594 MouseEvent.MOUSE_RELEASED == e.getID() ||
595 MouseEvent.MOUSE_CLICKED == e.getID()) {
596 addIdleTimeCounterRequest();
597 for (Runnable activityListener : myActivityListeners) {
598 activityListener.run();
603 if (myPopupManager.isPopupActive() && myPopupManager.dispatch(e)) {
604 if (myKeyEventDispatcher.isWaitingForSecondKeyStroke()) {
605 myKeyEventDispatcher.setState(KeyState.STATE_INIT);
611 for (EventDispatcher eachDispatcher : myDispatchers) {
612 if (eachDispatcher.dispatch(e)) {
617 if (e instanceof InputMethodEvent) {
618 if (SystemInfo.isMac && myKeyEventDispatcher.isWaitingForSecondKeyStroke()) {
622 if (e instanceof ComponentEvent && myWindowManager != null) {
623 myWindowManager.dispatchComponentEvent((ComponentEvent)e);
625 if (e instanceof KeyEvent) {
626 if (mySuspendMode || !myKeyEventDispatcher.dispatchKeyEvent((KeyEvent)e)) {
627 defaultDispatchEvent(e);
630 ((KeyEvent)e).consume();
631 defaultDispatchEvent(e);
634 else if (e instanceof MouseEvent) {
635 MouseEvent me = (MouseEvent)e;
636 if (IdeMouseEventDispatcher.patchClickCount(me) && me.getID() == MouseEvent.MOUSE_CLICKED) {
637 final MouseEvent toDispatch =
638 new MouseEvent(me.getComponent(), me.getID(), System.currentTimeMillis(), me.getModifiers(), me.getX(), me.getY(), 1,
639 me.isPopupTrigger(), me.getButton());
640 //noinspection SSBasedInspection
641 SwingUtilities.invokeLater(new Runnable() {
644 dispatchEvent(toDispatch);
648 if (!myMouseEventDispatcher.dispatchMouseEvent(me)) {
649 defaultDispatchEvent(e);
653 defaultDispatchEvent(e);
657 private static final Field ourInvocationEventRunnableAccessor;
661 if (Registry.is("trace.clipboard.events") && SystemInfo.isJavaVersionAtLeast("1.8.0_60")) {
663 field = InvocationEvent.class.getDeclaredField("runnable");
664 field.setAccessible(true);
666 catch (Exception e) {
667 LOG.warn("Error creating accessor for java.awt.event.InvocationEvent.runnable field", e);
670 ourInvocationEventRunnableAccessor = field;
673 private static void traceClipboardEvents(AWTEvent e) {
674 if (ourInvocationEventRunnableAccessor != null && e instanceof InvocationEvent && e.getClass().getName().equals("sun.awt.PeerEvent")) {
676 Object r = ourInvocationEventRunnableAccessor.get(e);
678 String className = r.getClass().getName();
679 // This check for event constructed in SunClipboard.lostOwnershipLater() (known to work with JDK 8u60)
680 if (className.contains("sun.awt.datatransfer.SunClipboard") && className.contains("Lambda")) {
681 LOG.info("Clipboard has been set by other application");
685 catch (Exception ex) {
686 LOG.warn("Error accessing java.awt.event.InvocationEvent.runnable field");
691 private static void fixStickyWindow(KeyboardFocusManager mgr, Window wnd, String resetMethod) {
692 Window showingWindow = wnd;
694 if (wnd != null && !wnd.isShowing()) {
695 while (showingWindow != null) {
696 if (showingWindow.isShowing()) break;
697 showingWindow = (Window)showingWindow.getParent();
700 if (showingWindow == null) {
701 final Frame[] allFrames = Frame.getFrames();
702 for (Frame each : allFrames) {
703 if (each.isShowing()) {
704 showingWindow = each;
711 if (showingWindow != null && showingWindow != wnd) {
712 final Method setActive = ReflectionUtil.findMethod(ReflectionUtil.getClassDeclaredMethods(KeyboardFocusManager.class, false), resetMethod, Window.class);
713 if (setActive != null) {
715 setActive.invoke(mgr, (Window)showingWindow);
717 catch (Exception exc) {
725 public void fixStickyFocusedComponents(@Nullable AWTEvent e) {
726 if (e != null && !(e instanceof InputEvent)) return;
728 final KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
730 if (Registry.is("actionSystem.fixStickyFocusedWindows")) {
731 fixStickyWindow(mgr, mgr.getActiveWindow(), "setGlobalActiveWindow");
732 fixStickyWindow(mgr, mgr.getFocusedWindow(), "setGlobalFocusedWindow");
735 if (Registry.is("actionSystem.fixNullFocusedComponent")) {
736 final Component focusOwner = mgr.getFocusOwner();
737 if (focusOwner == null || !focusOwner.isShowing() || focusOwner instanceof JFrame || focusOwner instanceof JDialog) {
739 final Application app = ApplicationManager.getApplication();
740 if (app instanceof ApplicationEx && !((ApplicationEx) app).isLoaded()) {
744 boolean mouseEventsAhead = isMouseEventAhead(e);
745 boolean focusTransferredNow = IdeFocusManager.getGlobalInstance().isFocusBeingTransferred();
747 boolean okToFixFocus = !mouseEventsAhead && !focusTransferredNow;
750 Window showingWindow = mgr.getActiveWindow();
751 if (showingWindow == null) {
752 Method getNativeFocusOwner = ReflectionUtil.getDeclaredMethod(KeyboardFocusManager.class, "getNativeFocusOwner");
753 if (getNativeFocusOwner != null) {
755 Object owner = getNativeFocusOwner.invoke(mgr);
756 if (owner instanceof Component) {
757 showingWindow = UIUtil.getWindow((Component)owner);
760 catch (Exception e1) {
765 if (showingWindow != null) {
766 final IdeFocusManager fm = IdeFocusManager.findInstanceByComponent(showingWindow);
767 ExpirableRunnable maybeRequestDefaultFocus = new ExpirableRunnable() {
770 if (getPopupManager().requestDefaultFocus(false)) return;
772 final Application app = ApplicationManager.getApplication();
773 if (app != null && app.isActive()) {
774 fm.requestDefaultFocus(false);
779 public boolean isExpired() {
780 return !UIUtil.isMeaninglessFocusOwner(mgr.getFocusOwner());
783 fm.revalidateFocus(maybeRequestDefaultFocus);
790 public static boolean isMouseEventAhead(@Nullable AWTEvent e) {
791 IdeEventQueue queue = getInstance();
792 return e instanceof MouseEvent ||
793 queue.peekEvent(MouseEvent.MOUSE_PRESSED) != null ||
794 queue.peekEvent(MouseEvent.MOUSE_RELEASED) != null ||
795 queue.peekEvent(MouseEvent.MOUSE_CLICKED) != null;
798 private void enterSuspendModeIfNeeded(AWTEvent e) {
799 if (e instanceof KeyEvent) {
800 if (!mySuspendMode && shallEnterSuspendMode()) {
806 private boolean shallEnterSuspendMode() {
807 return peekEvent(WindowEvent.WINDOW_OPENED) != null;
810 private static boolean processAppActivationEvents(AWTEvent e) {
812 if (e instanceof WindowEvent) {
813 final WindowEvent we = (WindowEvent)e;
815 ApplicationActivationStateManager.get().updateState(we);
817 storeLastFocusedComponent(we);
823 private static void storeLastFocusedComponent(WindowEvent we) {
824 final Window eventWindow = we.getWindow();
826 if (we.getID() == WindowEvent.WINDOW_DEACTIVATED || we.getID() == WindowEvent.WINDOW_LOST_FOCUS) {
827 Component frame = UIUtil.findUltimateParent(eventWindow);
828 Component focusOwnerInDeactivatedWindow = eventWindow.getMostRecentFocusOwner();
829 IdeFrame[] allProjectFrames = WindowManager.getInstance().getAllProjectFrames();
831 if (focusOwnerInDeactivatedWindow != null) {
832 for (IdeFrame ideFrame : allProjectFrames) {
833 JFrame aFrame = WindowManager.getInstance().getFrame(ideFrame.getProject());
834 if (aFrame.equals(frame)) {
835 IdeFocusManager focusManager = IdeFocusManager.getGlobalInstance();
836 if (focusManager instanceof FocusManagerImpl) {
837 ((FocusManagerImpl)focusManager).setLastFocusedAtDeactivation(ideFrame, focusOwnerInDeactivatedWindow);
845 private void defaultDispatchEvent(final AWTEvent e) {
847 myDispatchingFocusEvent = e instanceof FocusEvent;
852 super.dispatchEvent(e);
854 catch (Throwable t) {
858 myDispatchingFocusEvent = false;
862 private static Field ourStickyAltField;
864 private static void fixStickyAlt(AWTEvent e) {
865 if (Registry.is("actionSystem.win.suppressAlt.new")) {
866 if (UIUtil.isUnderWindowsLookAndFeel() &&
867 e instanceof InputEvent &&
868 (((InputEvent)e).getModifiers() & (InputEvent.ALT_MASK | InputEvent.ALT_DOWN_MASK)) != 0 &&
869 !(e instanceof KeyEvent && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_ALT)) {
871 if (ourStickyAltField == null) {
872 Class<?> aClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsRootPaneUI$AltProcessor");
873 ourStickyAltField = ReflectionUtil.getDeclaredField(aClass, "menuCanceledOnPress");
875 if (ourStickyAltField != null) {
876 ourStickyAltField.set(null, true);
879 catch (Exception exception) {
880 LOG.error(exception);
884 else if (SystemInfo.isWinXpOrNewer && !SystemInfo.isWinVistaOrNewer && e instanceof KeyEvent && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_ALT) {
885 ((KeyEvent)e).consume(); // IDEA-17359
889 public boolean isDispatchingFocusEvent() {
890 return myDispatchingFocusEvent;
893 private static boolean typeAheadDispatchToFocusManager(AWTEvent e) {
894 if (e instanceof KeyEvent) {
895 final KeyEvent event = (KeyEvent)e;
896 if (!event.isConsumed()) {
897 final IdeFocusManager focusManager = IdeFocusManager.findInstanceByComponent(event.getComponent());
898 return focusManager.dispatch(event);
905 public void flushQueue() {
907 AWTEvent event = peekEvent();
908 if (event == null) return;
910 AWTEvent event1 = getNextEvent();
911 dispatchEvent(event1);
913 catch (Exception e) {
919 public void pumpEventsForHierarchy(Component modalComponent, Condition<AWTEvent> exitCondition) {
923 event = getNextEvent();
924 boolean eventOk = true;
925 if (event instanceof InputEvent) {
926 final Object s = event.getSource();
927 if (s instanceof Component) {
928 Component c = (Component)s;
929 Window modalWindow = modalComponent == null ? null : SwingUtilities.windowForComponent(modalComponent);
930 while (c != null && c != modalWindow) c = c.getParent();
933 ((InputEvent)event).consume();
939 dispatchEvent(event);
942 catch (Throwable e) {
947 while (!exitCondition.value(event));
951 public interface EventDispatcher {
952 boolean dispatch(AWTEvent e);
956 private final class MyFireIdleRequest implements Runnable {
957 private final Runnable myRunnable;
958 private final int myTimeout;
961 public MyFireIdleRequest(@NotNull Runnable runnable, final int timeout) {
963 myRunnable = runnable;
970 synchronized (myLock) {
971 if (myIdleListeners.contains(myRunnable)) // do not reschedule if not interested anymore
973 myIdleRequestsAlarm.addRequest(this, myTimeout, ModalityState.NON_MODAL);
978 public int getTimeout() {
983 public String toString() {
984 return "Fire idle request. delay: "+getTimeout()+"; runnable: "+myRunnable;
988 private final class ExitSuspendModeRunnable implements Runnable {
999 public long getIdleTime() {
1004 public IdePopupManager getPopupManager() {
1005 return myPopupManager;
1008 public IdeKeyEventDispatcher getKeyEventDispatcher() {
1009 return myKeyEventDispatcher;
1013 * Same as {@link #blockNextEvents(MouseEvent, IdeEventQueue.BlockMode)} with <code>blockMode</code> equal to <code>COMPLETE</code>.
1015 public void blockNextEvents(final MouseEvent e) {
1016 blockNextEvents(e, BlockMode.COMPLETE);
1020 * When <code>blockMode</code> is <code>COMPLETE</code>, blocks following related mouse events completely, when <code>blockMode</code> is
1021 * <code>ACTIONS</code> only blocks performing actions bound to corresponding mouse shortcuts.
1023 public void blockNextEvents(final MouseEvent e, BlockMode blockMode) {
1024 myMouseEventDispatcher.blockNextEvents(e, blockMode);
1027 public boolean isSuspendMode() {
1028 return mySuspendMode;
1031 public boolean hasFocusEventsPending() {
1032 return peekEvent(FocusEvent.FOCUS_GAINED) != null || peekEvent(FocusEvent.FOCUS_LOST) != null;
1035 private boolean isReady() {
1036 return !myKeyboardBusy && myKeyEventDispatcher.isReady();
1039 public void maybeReady() {
1043 private void flushReady() {
1044 if (myReady.isEmpty() || !isReady()) return;
1046 Runnable[] ready = myReady.toArray(new Runnable[myReady.size()]);
1049 for (Runnable each : ready) {
1054 public void doWhenReady(final Runnable runnable) {
1055 if (EventQueue.isDispatchThread()) {
1056 myReady.add(runnable);
1060 //noinspection SSBasedInspection
1061 SwingUtilities.invokeLater(new Runnable() {
1064 myReady.add(runnable);
1071 public boolean isPopupActive() {
1072 return myPopupManager.isPopupActive();
1075 private static class WindowsAltSuppressor implements EventDispatcher {
1076 private boolean myWaitingForAltRelease;
1077 private Robot myRobot;
1080 public boolean dispatch(AWTEvent e) {
1081 boolean dispatch = true;
1082 if (e instanceof KeyEvent) {
1083 KeyEvent ke = (KeyEvent)e;
1084 final Component component = ke.getComponent();
1085 boolean pureAlt = ke.getKeyCode() == KeyEvent.VK_ALT && (ke.getModifiers() | InputEvent.ALT_MASK) == InputEvent.ALT_MASK;
1087 myWaitingForAltRelease = false;
1090 if (ApplicationManager.getApplication() == null ||
1091 UISettings.getInstance() == null ||
1092 !SystemInfo.isWindows ||
1093 !Registry.is("actionSystem.win.suppressAlt") ||
1094 !(UISettings.getInstance().HIDE_TOOL_STRIPES || UISettings.getInstance().PRESENTATION_MODE)) {
1098 if (ke.getID() == KeyEvent.KEY_PRESSED) {
1099 dispatch = !myWaitingForAltRelease;
1101 else if (ke.getID() == KeyEvent.KEY_RELEASED) {
1102 if (myWaitingForAltRelease) {
1103 myWaitingForAltRelease = false;
1106 else if (component != null) {
1107 //noinspection SSBasedInspection
1108 SwingUtilities.invokeLater(new Runnable() {
1112 final Window window = UIUtil.getWindow(component);
1113 if (window == null || !window.isActive()) {
1116 myWaitingForAltRelease = true;
1117 if (myRobot == null) {
1118 myRobot = new Robot();
1120 myRobot.keyPress(KeyEvent.VK_ALT);
1121 myRobot.keyRelease(KeyEvent.VK_ALT);
1123 catch (AWTException e1) {
1137 public boolean isInputMethodEnabled() {
1138 return !SystemInfo.isMac || myInputMethodLock == 0;
1141 public void disableInputMethods(Disposable parentDisposable) {
1142 myInputMethodLock++;
1143 Disposer.register(parentDisposable, new Disposable() {
1145 public void dispose() {
1146 myInputMethodLock--;
1151 private final FrequentEventDetector myFrequentEventDetector = new FrequentEventDetector(1009, 100);
1153 public void postEvent(@NotNull AWTEvent theEvent) {
1154 myFrequentEventDetector.eventHappened();
1155 super.postEvent(theEvent);
1159 public AWTEvent peekEvent() {
1160 AWTEvent event = super.peekEvent();
1161 if (event != null) {
1164 if (isTestMode() && LaterInvocator.ensureFlushRequested()) {
1165 return super.peekEvent();
1170 private Boolean myTestMode;
1171 private boolean isTestMode() {
1172 Boolean testMode = myTestMode;
1173 if (testMode != null) return testMode;
1175 Application application = ApplicationManager.getApplication();
1176 if (application == null) return false;
1178 testMode = application.isUnitTestMode();
1179 myTestMode = testMode;
1184 * @see IdeEventQueue#blockNextEvents(MouseEvent, IdeEventQueue.BlockMode)
1186 public enum BlockMode {