blob: 1f96a3fa25593a1b159e741a80c0979ae0afc849 [file] [log] [blame]
/*
* Copyright 2000-2013 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.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.application.impl.LaterInvocator;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.ManagingFS;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
import com.intellij.openapi.vfs.newvfs.RefreshQueue;
import com.intellij.openapi.vfs.newvfs.RefreshSession;
import com.intellij.util.Alarm;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Anton Katilin
* @author Vladimir Kondratyev
*/
public class SaveAndSyncHandlerImpl implements ApplicationComponent, SaveAndSyncHandler {
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.SaveAndSyncHandler");
private final Runnable myIdleListener;
private final PropertyChangeListener myGeneralSettingsListener;
private final ProgressManager myProgressManager;
private final AtomicInteger myBlockSaveOnFrameDeactivationCount = new AtomicInteger();
private final AtomicInteger myBlockSyncOnFrameActivationCount = new AtomicInteger();
private final Alarm myRefreshDelayAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
private long myRefreshSessionId = 0;
public static SaveAndSyncHandlerImpl getInstance(){
return (SaveAndSyncHandlerImpl) ApplicationManager.getApplication().getComponent(SaveAndSyncHandler.class);
}
public SaveAndSyncHandlerImpl(final FrameStateManager frameStateManager,
final FileDocumentManager fileDocumentManager,
final GeneralSettings generalSettings,
final ProgressManager progressManager) {
myProgressManager = progressManager;
myIdleListener = new Runnable() {
@Override
public void run() {
if (generalSettings.isAutoSaveIfInactive() && canSyncOrSave()) {
((FileDocumentManagerImpl)fileDocumentManager).saveAllDocuments(false);
}
}
};
IdeEventQueue.getInstance().addIdleListener(
myIdleListener,
generalSettings.getInactiveTimeout() * 1000
);
myGeneralSettingsListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent e) {
if (GeneralSettings.PROP_INACTIVE_TIMEOUT.equals(e.getPropertyName())) {
IdeEventQueue eventQueue = IdeEventQueue.getInstance();
eventQueue.removeIdleListener(myIdleListener);
Integer timeout = (Integer)e.getNewValue();
eventQueue.addIdleListener(myIdleListener, timeout.intValue() * 1000);
}
}
};
generalSettings.addPropertyChangeListener(myGeneralSettingsListener);
frameStateManager.addListener(new FrameStateListener() {
@Override
public void onFrameDeactivated() {
if (canSyncOrSave()) {
saveProjectsAndDocuments();
}
}
@Override
public void onFrameActivated() {
refreshFiles();
}
});
}
@Override
@NotNull
public String getComponentName() {
return "SaveAndSyncHandler";
}
@Override
public void initComponent() {
}
@Override
public void disposeComponent() {
GeneralSettings.getInstance().removePropertyChangeListener(myGeneralSettingsListener);
IdeEventQueue.getInstance().removeIdleListener(myIdleListener);
}
private boolean canSyncOrSave() {
return !LaterInvocator.isInModalContext() && !myProgressManager.hasModalProgressIndicator();
}
// made public for tests
public void saveProjectsAndDocuments() {
if (LOG.isDebugEnabled()) {
LOG.debug("enter: save()");
}
if (ApplicationManager.getApplication().isDisposed()) return;
if (myBlockSaveOnFrameDeactivationCount.get() == 0 && GeneralSettings.getInstance().isSaveOnFrameDeactivation()) {
FileDocumentManager.getInstance().saveAllDocuments();
Project[] openProjects = ProjectManagerEx.getInstanceEx().getOpenProjects();
for (Project project : openProjects) {
if (LOG.isDebugEnabled()) {
LOG.debug("save project: " + project);
}
project.save();
}
if (LOG.isDebugEnabled()) {
LOG.debug("save application settings");
}
ApplicationManagerEx.getApplicationEx().saveSettings();
if (LOG.isDebugEnabled()) {
LOG.debug("exit: save()");
}
}
}
private void refreshFiles() {
if (ApplicationManager.getApplication().isDisposed() || !GeneralSettings.getInstance().isSyncOnFrameActivation()) {
return;
}
LOG.debug("enter: refreshFiles()");
myRefreshDelayAlarm.cancelAllRequests();
myRefreshDelayAlarm.addRequest(new Runnable() {
@Override
public void run() {
if (canSyncOrSave()) {
refreshOpenFiles();
}
maybeRefresh(ModalityState.NON_MODAL);
}
}, 300, ModalityState.NON_MODAL);
LOG.debug("exit: refreshFiles()");
}
public void maybeRefresh(@NotNull ModalityState modalityState) {
if (myBlockSyncOnFrameActivationCount.get() == 0 && GeneralSettings.getInstance().isSyncOnFrameActivation()) {
LOG.debug("VFS refresh started");
RefreshQueue queue = RefreshQueue.getInstance();
queue.cancelSession(myRefreshSessionId);
RefreshSession session = queue.createSession(true, true, null, modalityState);
session.addAllFiles(ManagingFS.getInstance().getLocalRoots());
myRefreshSessionId = session.getId();
session.launch();
LOG.debug("VFS refresh finished");
}
}
public static void refreshOpenFiles() {
List<VirtualFile> files = ContainerUtil.newArrayList();
for (Project project : ProjectManager.getInstance().getOpenProjects()) {
VirtualFile[] projectFiles = FileEditorManager.getInstance(project).getSelectedFiles();
for (VirtualFile file : projectFiles) {
if (file instanceof NewVirtualFile) {
files.add(file);
}
}
}
if (!files.isEmpty()) {
// refresh open files synchronously so it doesn't wait for potentially longish refresh request in the queue to finish
RefreshQueue.getInstance().refresh(false, false, null, files);
}
}
@Override
public void blockSaveOnFrameDeactivation() {
myBlockSaveOnFrameDeactivationCount.incrementAndGet();
}
@Override
public void unblockSaveOnFrameDeactivation() {
myBlockSaveOnFrameDeactivationCount.decrementAndGet();
}
@Override
public void blockSyncOnFrameActivation() {
myBlockSyncOnFrameActivationCount.incrementAndGet();
}
@Override
public void unblockSyncOnFrameActivation() {
myBlockSyncOnFrameActivationCount.decrementAndGet();
}
}