IDEA-142305 Changes to files are not saved on frame deactivation

This commit is contained in:
Denis Fokin
2015-07-07 18:04:12 +03:00
parent 4f67bb89a0
commit ea24efaba3
10 changed files with 159 additions and 33 deletions
@@ -27,7 +27,7 @@ import java.util.regex.Pattern;
/**
* @author Konstantin Bulenkov
*/
public class UnscrambleListener implements ApplicationActivationListener {
public class UnscrambleListener extends ApplicationActivationListener.Adapter {
private static final int MAX_STACKTRACE_SIZE = 100 * 1024;
private String stacktrace = null;
@@ -78,7 +78,7 @@ public class QuickDocOnMouseOverManager {
ApplicationManager.getApplication().getMessageBus().connect().subscribe(
ApplicationActivationListener.TOPIC,
new ApplicationActivationListener() {
new ApplicationActivationListener.Adapter() {
@Override
public void applicationActivated(IdeFrame ideFrame) {
myApplicationActive = true;
@@ -34,11 +34,24 @@ 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) { }
}
}
@@ -50,7 +50,7 @@ public class FrameStateManagerImpl extends FrameStateManager {
myShouldSynchronize = false;
mySyncAlarm = new Alarm();
app.getMessageBus().connect().subscribe(ApplicationActivationListener.TOPIC, new ApplicationActivationListener() {
app.getMessageBus().connect().subscribe(ApplicationActivationListener.TOPIC, new ApplicationActivationListener.Adapter() {
@Override
public void applicationActivated(IdeFrame ideFrame) {
myActive.onReady();
@@ -811,12 +811,15 @@ public class IdeEventQueue extends EventQueue {
if (we.getID() == WindowEvent.WINDOW_ACTIVATED || we.getID() == WindowEvent.WINDOW_GAINED_FOCUS) {
appImpl.myCancelDeactivation = true;
if (!appImpl.isActive()) {
appImpl.tryToApplyActivationState(true, eventWindow);
OpenAPIAccessor.getApplicationImplAccessor().applyActivation(appImpl, eventWindow);
}
}
else if (we.getID() == WindowEvent.WINDOW_DEACTIVATED) {
requestToDeactivateTime.getAndSet(System.currentTimeMillis());
// For stuf that cannot wait timeout we notify about deactivation
OpenAPIAccessor.getApplicationImplAccessor().applyDeactivation(appImpl, eventWindow);
// 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
@@ -824,8 +827,8 @@ public class IdeEventQueue extends EventQueue {
Timer timer = new Timer(Registry.intValue("app.deactivation.timeout"), new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (appImpl.isActive() && !appImpl.isDeactivationCanceled()) {
appImpl.tryToApplyActivationState(false, eventWindow);
if (appImpl.isActiveDelayed() && !appImpl.isDeactivationCanceled()) {
OpenAPIAccessor.getApplicationImplAccessor().applyDelayedDeactivation(appImpl, eventWindow);
}
}
});
@@ -0,0 +1,64 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide;
import com.intellij.openapi.application.impl.ApplicationImpl;
import com.intellij.util.concurrency.AtomicFieldUpdater;
import sun.misc.Unsafe;
import java.awt.*;
/**
* Provides ability to access ApplicationImpl package-private methods
* without reflection
*/
public final class OpenAPIAccessor {
private OpenAPIAccessor() {}
private static ApplicationImplAccessor applicationImplAccessor;
private static final Unsafe unsafe = AtomicFieldUpdater.getUnsafe();
public interface ApplicationImplAccessor {
boolean applyActivation (ApplicationImpl app, Window window);
boolean applyDeactivation (ApplicationImpl app, Window window);
boolean applyDelayedDeactivation (ApplicationImpl app, Window window);
}
/*
* Set an accessor object for the com.intellij.openapi.application.impl.ApplicationImpl class.
*/
public static void setApplicationImplAccessor(ApplicationImplAccessor aia) {
applicationImplAccessor = aia;
}
/*
* Retrieve the accessor object for the com.intellij.openapi.application.impl.ApplicationImpl class.
*/
public static ApplicationImplAccessor getApplicationImplAccessor() {
if (applicationImplAccessor == null) {
unsafe.ensureClassInitialized(ApplicationImpl.class);
}
return applicationImplAccessor;
}
}
@@ -1420,7 +1420,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat
addActionListener(this);
setRepeats(true);
final MessageBusConnection connection = ApplicationManager.getApplication().getMessageBus().connect();
connection.subscribe(ApplicationActivationListener.TOPIC, new ApplicationActivationListener() {
connection.subscribe(ApplicationActivationListener.TOPIC, new ApplicationActivationListener.Adapter() {
@Override
public void applicationActivated(IdeFrame ideFrame) {
setDelay(TIMER_DELAY);
@@ -42,7 +42,7 @@ import java.awt.*;
* @author Anton Katilin
* @author Vladimir Kondratyev
*/
public final class ActionPopupMenuImpl implements ActionPopupMenu, ApplicationActivationListener {
public final class ActionPopupMenuImpl extends ApplicationActivationListener.Adapter implements ActionPopupMenu {
private final MyMenu myMenu;
private final ActionManagerImpl myManager;
@@ -131,10 +131,36 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
@NonNls 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;
static {
OpenAPIAccessor.setApplicationImplAccessor(new OpenAPIAccessor.ApplicationImplAccessor() {
@Override
public boolean applyActivation(ApplicationImpl app, Window window) {
return app.applyActivation(window);
}
@Override
public boolean applyDeactivation(ApplicationImpl app, Window window) {
return app.applyDeactivation(window);
}
@Override
public boolean applyDelayedDeactivation(ApplicationImpl app, Window window) {
return app.applyDelayedDeactivation(window);
}
});
}
public boolean isActiveDelayed() {
return myActiveDelayed;
}
private static class Status {
// higher three bits are for IS_* flags
// lower bits are for edtSafe counter
@@ -1177,28 +1203,47 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
return myCancelDeactivation;
}
public boolean tryToApplyActivationState(boolean active, Window window) {
final Component frame = UIUtil.findUltimateParent(window);
boolean applyActivation (Window window) {
if (!isActive()) {
myActive = true;
myActiveDelayed = true;
IdeFrame ideFrame = getIdeFrameFromWindow(window);
if (ideFrame != null) {
getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC).applicationActivated(ideFrame);
}
}
return false;
}
if (frame instanceof IdeFrame) {
final IdeFrame ideFrame = (IdeFrame)frame;
if (isActive() != active) {
myActive = active;
System.setProperty("idea.active", String.valueOf(myActive));
ApplicationActivationListener publisher = getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC);
if (active) {
publisher.applicationActivated(ideFrame);
}
else {
publisher.applicationDeactivated(ideFrame);
}
boolean applyDeactivation (Window window) {
if (isActive()) {
myActive = false;
IdeFrame ideFrame = getIdeFrameFromWindow(window);
if (ideFrame != null) {
getMessageBus().syncPublisher(ApplicationActivationListener.TOPIC).applicationDeactivated(ideFrame);
return true;
}
}
return false;
}
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;
}
@Override
public boolean isActive() {
if (isUnitTestMode()) return true;
@@ -1068,16 +1068,7 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable {
callback.setRejected();
}
private class AppListener implements ApplicationActivationListener {
@Override
public void applicationDeactivated(IdeFrame ideFrame) {
final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
Component parent = UIUtil.findUltimateParent(owner);
if (parent == ideFrame) {
myLastFocusedAtDeactivation.put(ideFrame, owner);
}
}
private class AppListener extends ApplicationActivationListener.Adapter {
@Override
public void applicationActivated(final IdeFrame ideFrame) {
@@ -1093,6 +1084,16 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable {
}
}
@Override
public void delayedApplicationDeactivated(IdeFrame ideFrame) {
final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
Component parent = UIUtil.findUltimateParent(owner);
if (parent == ideFrame) {
myLastFocusedAtDeactivation.put(ideFrame, owner);
}
}
private void focusLastFocusedComponent(IdeFrame ideFrame) {
final KeyboardFocusManager mgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
if (mgr.getFocusOwner() == null) {