diff --git a/platform/platform-api/src/com/intellij/openapi/application/ApplicationActivationListener.java b/platform/platform-api/src/com/intellij/openapi/application/ApplicationActivationListener.java index 5ad5f6e569e8..b3bd61717912 100644 --- a/platform/platform-api/src/com/intellij/openapi/application/ApplicationActivationListener.java +++ b/platform/platform-api/src/com/intellij/openapi/application/ApplicationActivationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,24 +34,11 @@ public interface ApplicationActivationListener { */ void applicationDeactivated(IdeFrame ideFrame); - /** - * This is more precise notification than {code applicationDeactivated} callback. - * It is intended for focus subsystem and purposes where we do not want - * to be bothered by false application deactivation events. - * - * The shortcoming of the method is that a notification is delivered - * with a delay. See {code app.deactivation.timeout} key in the registry - */ - void delayedApplicationDeactivated(IdeFrame ideFrame); - abstract class Adapter implements ApplicationActivationListener { @Override public void applicationActivated(IdeFrame ideFrame) { } @Override public void applicationDeactivated(IdeFrame ideFrame) { } - - @Override - public void delayedApplicationDeactivated(IdeFrame ideFrame) { } } } diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java index 38b4efc7b3db..35d1ee7b34a9 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java @@ -39,10 +39,7 @@ import com.intellij.openapi.util.ExpirableRunnable; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.IdeFocusManager; -import com.intellij.openapi.wm.IdeFrame; -import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.WindowManagerEx; -import com.intellij.openapi.wm.impl.FocusManagerImpl; import com.intellij.util.Alarm; import com.intellij.util.ReflectionUtil; import com.intellij.util.containers.ContainerUtil; @@ -65,7 +62,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; /** * @author Vladimir Kondratyev @@ -540,7 +536,9 @@ public class IdeEventQueue extends EventQueue { myEventCount++; - if (processAppActivationEvents(e)) return; + if (e instanceof WindowEvent) { + processAppActivationEvents((WindowEvent)e); + } if (!typeAheadFlushing) { fixStickyFocusedComponents(e); @@ -794,72 +792,35 @@ public class IdeEventQueue extends EventQueue { return peekEvent(WindowEvent.WINDOW_OPENED) != null; } - private static AtomicLong requestToDeactivateTime = new AtomicLong(System.currentTimeMillis()); + private static final int APP_DEACTIVATION_DELAY = 50; + private static final Alarm ourDeactivationAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); - private static boolean processAppActivationEvents(AWTEvent e) { - Application app = ApplicationManager.getApplication(); - if (!(app instanceof ApplicationImpl)) return false; - final ApplicationImpl appImpl = (ApplicationImpl)app; - - if (e instanceof WindowEvent) { - final WindowEvent we = (WindowEvent)e; - - // Only Dialogs and Frames get window activated events. - // Let's handle Windows this way. - - final Window eventWindow = we.getWindow(); - if (we.getID() == WindowEvent.WINDOW_ACTIVATED || we.getID() == WindowEvent.WINDOW_GAINED_FOCUS) { - //if () { - // getPopupManager().closeAllPopups(); - //} - appImpl.myCancelDeactivation = true; - if (!appImpl.isActive()) { - appImpl.tryToApplyActivationState(eventWindow, true, false); + /* + * This method processes "external" focus events (i.e. those induced by a window manager) and tracks + * an application activation/deactivation state from them (an application is active if at least one it's windows has the focus). + * + * One special case is closing a child dialog: it generates a focus-lost event immediately followed by a focus-gained one. + * To avoid (or rather reduce a chance of) unneeded app-deactivation event the processing of the focus-lost event + * is slightly delayed. + */ + private static void processAppActivationEvents(final WindowEvent e) { + if ((e.getID() == WindowEvent.WINDOW_GAINED_FOCUS || e.getID() == WindowEvent.WINDOW_LOST_FOCUS) && e.getOppositeWindow() == null) { + final Application app = ApplicationManager.getApplication(); + if (app instanceof ApplicationImpl) { + if (e.getID() == WindowEvent.WINDOW_GAINED_FOCUS) { + ourDeactivationAlarm.cancelAllRequests(); + ((ApplicationImpl)app).tryToApplyActivationState(e.getWindow(), true); } - } - else if (we.getID() == WindowEvent.WINDOW_DEACTIVATED) { - requestToDeactivateTime.getAndSet(System.currentTimeMillis()); - - // For stuff that cannot wait we notify about deactivation immediately - appImpl.tryToApplyActivationState(eventWindow, false, true); - - // We do not know for sure that application is going to be inactive, - // we could just be showing a popup or another transient window. - // So let's postpone the application deactivation for a while - appImpl.myCancelDeactivation = false; - - Timer timer = new Timer(Registry.intValue("app.deactivation.timeout"), new ActionListener() { - public void actionPerformed(ActionEvent evt) { - if (appImpl.isActiveDelayed() && !appImpl.isDeactivationCanceled()) { - appImpl.tryToApplyActivationState(eventWindow, false, false); + else { + ourDeactivationAlarm.addRequest(new Runnable() { + @Override + public void run() { + ((ApplicationImpl)app).tryToApplyActivationState(e.getWindow(), false); } - } - }); - - timer.setRepeats(false); - timer.start(); - } - - if (we.getID() == WindowEvent.WINDOW_DEACTIVATED || we.getID() == WindowEvent.WINDOW_LOST_FOCUS) { - Component frame = UIUtil.findUltimateParent(eventWindow); - Component focusOwnerInDeactivatedWindow = eventWindow.getMostRecentFocusOwner(); - IdeFrame[] allProjectFrames = WindowManager.getInstance().getAllProjectFrames(); - - if (focusOwnerInDeactivatedWindow != null) { - for (IdeFrame ideFrame : allProjectFrames) { - JFrame aFrame = WindowManager.getInstance().getFrame(ideFrame.getProject()); - if (aFrame.equals(frame)) { - IdeFocusManager focusManager = IdeFocusManager.getGlobalInstance(); - if (focusManager instanceof FocusManagerImpl) { - ((FocusManagerImpl)focusManager).setLastFocusedAtDeactivation(ideFrame, focusOwnerInDeactivatedWindow); - } - } - } + }, APP_DEACTIVATION_DELAY); } } } - - return false; } private void defaultDispatchEvent(final AWTEvent e) { diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index 4f688cd5e019..350668214dc3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -129,16 +129,10 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App private static final String WAS_EVER_SHOWN = "was.ever.shown"; private volatile boolean myActive; - private volatile boolean myActiveDelayed; - public volatile boolean myCancelDeactivation; private static final int IS_EDT_FLAG = 1<<30; // we don't mess with sign bit since we want to do arithmetic private static final int IS_READ_LOCK_ACQUIRED_FLAG = 1<<29; - public boolean isActiveDelayed() { - return myActiveDelayed; - } - private static class Status { // higher three bits are for IS_* flags // lower bits are for edtSafe counter @@ -1193,10 +1187,6 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App return true; } - public boolean isDeactivationCanceled() { - return myCancelDeactivation; - } - /** * !!!!! CAUTION !!!!! * !!!!! CAUTION !!!!! @@ -1212,58 +1202,31 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App * * There are no legitimate usages of the method outside of IdeEventQueue.processAppActivationEvents() */ - public boolean tryToApplyActivationState(Window window, boolean activation, boolean immediate) { - return activation ? applyActivation(window) : - immediate ? applyDeactivation(window) - : applyDelayedDeactivation(window); - } - - private boolean applyActivation(Window window) { - if (!isActive()) { + public void tryToApplyActivationState(Window window, boolean activation) { + if (activation && !isActive()) { myActive = true; - myActiveDelayed = true; IdeFrame ideFrame = getIdeFrameFromWindow(window); if (ideFrame != null) { getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC).applicationActivated(ideFrame); } } - return false; - } - - private boolean applyDeactivation(Window window) { - if (isActive()) { + else if (!activation && isActive()) { myActive = false; IdeFrame ideFrame = getIdeFrameFromWindow(window); if (ideFrame != null) { getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC).applicationDeactivated(ideFrame); - return true; } } - return false; } - private boolean applyDelayedDeactivation(Window window) { - if (isActiveDelayed()) { - myActiveDelayed = false; - IdeFrame ideFrame = getIdeFrameFromWindow(window); - if (ideFrame != null) { - getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC).delayedApplicationDeactivated(ideFrame); - return true; - } - } - return false; - } - - IdeFrame getIdeFrameFromWindow (Window window) { - final Component frame = UIUtil.findUltimateParent(window); - return (frame instanceof IdeFrame) ? (IdeFrame)frame : null; + private static IdeFrame getIdeFrameFromWindow(Window window) { + Component frame = UIUtil.findUltimateParent(window); + return frame instanceof IdeFrame ? (IdeFrame)frame : null; } @Override public boolean isActive() { - if (isUnitTestMode()) return true; - - return KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow() != null || myActive; + return isUnitTestMode() || myActive; } @NotNull diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java index 3e50443a24cd..36aeab68f038 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java @@ -1069,7 +1069,6 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } private class AppListener extends ApplicationActivationListener.Adapter { - @Override public void applicationActivated(final IdeFrame ideFrame) { final FocusCommand cmd = myFocusCommandOnAppActivation; @@ -1085,13 +1084,13 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } @Override - public void delayedApplicationDeactivated(IdeFrame ideFrame) { - final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); - Component parent = UIUtil.findUltimateParent(owner); + public void applicationDeactivated(IdeFrame ideFrame) { + final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + Component parent = UIUtil.findUltimateParent(owner); - if (parent == ideFrame) { - myLastFocusedAtDeactivation.put(ideFrame, owner); - } + if (parent == ideFrame) { + myLastFocusedAtDeactivation.put(ideFrame, owner); + } } private void focusLastFocusedComponent(IdeFrame ideFrame) { diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 50c436ae4d3b..cc183924808b 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -621,7 +621,6 @@ tests.view.old.statistics.panel=false tfs.set.connection.timeout=false -app.deactivation.timeout=1500 ide.mac.yosemite.laf=false ide.mac.yosemite.laf.restartRequired=true ide.mac.yosemite.laf.description=Replaces Aqua LaF to enhanced IntelliJ LaF