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.openapi.Disposable;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.application.ModalityState;
21 import com.intellij.openapi.application.impl.LaterInvocator;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.fileEditor.FileDocumentManager;
24 import com.intellij.openapi.fileEditor.FileEditorManager;
25 import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl;
26 import com.intellij.openapi.progress.ProgressManager;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.openapi.project.ProjectManager;
29 import com.intellij.openapi.project.ex.ProjectManagerEx;
30 import com.intellij.openapi.vfs.VirtualFile;
31 import com.intellij.openapi.vfs.newvfs.ManagingFS;
32 import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
33 import com.intellij.openapi.vfs.newvfs.RefreshQueue;
34 import com.intellij.openapi.vfs.newvfs.RefreshSession;
35 import com.intellij.util.SingleAlarm;
36 import com.intellij.util.containers.ContainerUtil;
37 import org.jetbrains.annotations.NotNull;
39 import java.beans.PropertyChangeEvent;
40 import java.beans.PropertyChangeListener;
41 import java.util.List;
42 import java.util.concurrent.atomic.AtomicInteger;
45 * @author Anton Katilin
46 * @author Vladimir Kondratyev
48 public class SaveAndSyncHandlerImpl extends SaveAndSyncHandler implements Disposable {
49 private static final Logger LOG = Logger.getInstance(SaveAndSyncHandler.class);
51 private final Runnable myIdleListener;
52 private final PropertyChangeListener myGeneralSettingsListener;
53 private final GeneralSettings mySettings;
54 private final ProgressManager myProgressManager;
55 private final SingleAlarm myRefreshDelayAlarm = new SingleAlarm(new Runnable() {
58 if (canSyncOrSave()) {
61 maybeRefresh(ModalityState.NON_MODAL);
65 private final AtomicInteger myBlockSaveOnFrameDeactivationCount = new AtomicInteger();
66 private final AtomicInteger myBlockSyncOnFrameActivationCount = new AtomicInteger();
67 private volatile long myRefreshSessionId = 0;
69 public SaveAndSyncHandlerImpl(@NotNull GeneralSettings generalSettings,
70 @NotNull ProgressManager progressManager,
71 @NotNull FrameStateManager frameStateManager,
72 @NotNull final FileDocumentManager fileDocumentManager) {
73 mySettings = generalSettings;
74 myProgressManager = progressManager;
76 myIdleListener = new Runnable() {
79 if (mySettings.isAutoSaveIfInactive() && canSyncOrSave()) {
80 ((FileDocumentManagerImpl)fileDocumentManager).saveAllDocuments(false);
84 IdeEventQueue.getInstance().addIdleListener(myIdleListener, mySettings.getInactiveTimeout() * 1000);
86 myGeneralSettingsListener = new PropertyChangeListener() {
88 public void propertyChange(@NotNull PropertyChangeEvent e) {
89 if (GeneralSettings.PROP_INACTIVE_TIMEOUT.equals(e.getPropertyName())) {
90 IdeEventQueue eventQueue = IdeEventQueue.getInstance();
91 eventQueue.removeIdleListener(myIdleListener);
92 Integer timeout = (Integer)e.getNewValue();
93 eventQueue.addIdleListener(myIdleListener, timeout.intValue() * 1000);
97 mySettings.addPropertyChangeListener(myGeneralSettingsListener);
99 frameStateManager.addListener(new FrameStateListener() {
101 public void onFrameDeactivated() {
102 LOG.debug("save(): enter");
103 if (canSyncOrSave()) {
104 saveProjectsAndDocuments();
106 LOG.debug("save(): exit");
110 public void onFrameActivated() {
111 if (!ApplicationManager.getApplication().isDisposed() && mySettings.isSyncOnFrameActivation()) {
119 public void dispose() {
120 RefreshQueue.getInstance().cancelSession(myRefreshSessionId);
121 mySettings.removePropertyChangeListener(myGeneralSettingsListener);
122 IdeEventQueue.getInstance().removeIdleListener(myIdleListener);
125 private boolean canSyncOrSave() {
126 return !LaterInvocator.isInModalContext() && !myProgressManager.hasModalProgressIndicator();
130 public void saveProjectsAndDocuments() {
131 if (!ApplicationManager.getApplication().isDisposed() &&
132 mySettings.isSaveOnFrameDeactivation() &&
133 myBlockSaveOnFrameDeactivationCount.get() == 0) {
134 doSaveDocumentsAndProjectsAndApp();
138 public static void doSaveDocumentsAndProjectsAndApp() {
139 LOG.debug("saving documents");
140 FileDocumentManager.getInstance().saveAllDocuments();
142 for (Project project : ProjectManagerEx.getInstanceEx().getOpenProjects()) {
143 if (LOG.isDebugEnabled()) {
144 LOG.debug("saving project: " + project);
149 LOG.debug("saving application settings");
150 ApplicationManager.getApplication().saveSettings();
154 public void scheduleRefresh() {
155 myRefreshDelayAlarm.cancelAndRequest();
158 public void maybeRefresh(@NotNull ModalityState modalityState) {
159 if (myBlockSyncOnFrameActivationCount.get() == 0 && mySettings.isSyncOnFrameActivation()) {
160 RefreshQueue queue = RefreshQueue.getInstance();
161 queue.cancelSession(myRefreshSessionId);
163 RefreshSession session = queue.createSession(true, true, null, modalityState);
164 session.addAllFiles(ManagingFS.getInstance().getLocalRoots());
165 myRefreshSessionId = session.getId();
167 LOG.debug("vfs refreshed");
169 else if (LOG.isDebugEnabled()) {
170 LOG.debug("vfs refresh rejected, blocked: " + (myBlockSyncOnFrameActivationCount.get() != 0)
171 + ", isSyncOnFrameActivation: " + mySettings.isSyncOnFrameActivation());
176 public void refreshOpenFiles() {
177 List<VirtualFile> files = ContainerUtil.newArrayList();
179 for (Project project : ProjectManager.getInstance().getOpenProjects()) {
180 for (VirtualFile file : FileEditorManager.getInstance(project).getSelectedFiles()) {
181 if (file instanceof NewVirtualFile) {
187 if (!files.isEmpty()) {
188 // refresh open files synchronously so it doesn't wait for potentially longish refresh request in the queue to finish
189 RefreshQueue.getInstance().refresh(false, false, null, files);
194 public void blockSaveOnFrameDeactivation() {
195 LOG.debug("save blocked");
196 myBlockSaveOnFrameDeactivationCount.incrementAndGet();
200 public void unblockSaveOnFrameDeactivation() {
201 myBlockSaveOnFrameDeactivationCount.decrementAndGet();
202 LOG.debug("save unblocked");
206 public void blockSyncOnFrameActivation() {
207 LOG.debug("sync blocked");
208 myBlockSyncOnFrameActivationCount.incrementAndGet();
212 public void unblockSyncOnFrameActivation() {
213 myBlockSyncOnFrameActivationCount.decrementAndGet();
214 LOG.debug("sync unblocked");