From 8aa8c822b71aae662b316a30003b4ea43798c1d5 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 25 Mar 2015 14:30:33 +0100 Subject: [PATCH] IDEA-138072 'Save' operation does nothing if there are unread 'could not save project' messages in Event Log --- .../impl/stores/ComponentStoreImpl.java | 1 - .../impl/stores/ProjectStoreImpl.java | 82 ++++++++++++------- .../openapi/project/impl/ProjectImpl.java | 62 +------------- .../project/impl/ProjectManagerImpl.java | 66 ++++++++++++--- 4 files changed, 109 insertions(+), 102 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java index 74270496bed6..d8c2820093dc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java @@ -185,7 +185,6 @@ public abstract class ComponentStoreImpl implements IComponentStore.Reloadable { errors = executeSave(session, readonlyFiles, errors); } } - return errors; } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java index 357eed947d85..a33f222855cc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java @@ -18,6 +18,7 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.CommonBundle; import com.intellij.ide.highlighter.ProjectFileType; import com.intellij.ide.highlighter.WorkspaceFileType; +import com.intellij.notification.Notifications; import com.intellij.notification.NotificationsManager; import com.intellij.openapi.application.*; import com.intellij.openapi.components.*; @@ -30,6 +31,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.project.impl.ProjectImpl; import com.intellij.openapi.project.impl.ProjectManagerImpl; +import com.intellij.openapi.project.impl.ProjectManagerImpl.UnableToSaveProjectNotification; import com.intellij.openapi.ui.MessageDialogBuilder; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Pair; @@ -446,51 +448,69 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject @Override protected final List doSave(@Nullable List saveSessions, @NotNull List> readonlyFiles, @Nullable List errors) { - ProjectImpl.UnableToSaveProjectNotification[] notifications = - NotificationsManager.getNotificationsManager().getNotificationsOfType(ProjectImpl.UnableToSaveProjectNotification.class, myProject); - if (notifications.length > 0) { - throw new SaveCancelledException(); - } - beforeSave(readonlyFiles); super.doSave(saveSessions, readonlyFiles, errors); - if (!readonlyFiles.isEmpty()) { - ReadonlyStatusHandler.OperationStatus status; - AccessToken token = ReadAction.start(); - try { - status = ReadonlyStatusHandler.getInstance(myProject).ensureFilesWritable(getFilesList(readonlyFiles)); + UnableToSaveProjectNotification[] notifications = + NotificationsManager.getNotificationsManager().getNotificationsOfType(UnableToSaveProjectNotification.class, myProject); + if (readonlyFiles.isEmpty()) { + if (notifications.length > 0) { + for (UnableToSaveProjectNotification notification : notifications) { + notification.expire(); + } } - finally { - token.finish(); + return errors; + } + + if (notifications.length > 0) { + throw new SaveCancelledException(); + } + + ReadonlyStatusHandler.OperationStatus status; + AccessToken token = ReadAction.start(); + try { + status = ReadonlyStatusHandler.getInstance(myProject).ensureFilesWritable(getFilesList(readonlyFiles)); + } + finally { + token.finish(); + } + + if (status.hasReadonlyFiles()) { + dropUnableToSaveProjectNotification(myProject, status.getReadonlyFiles()); + throw new SaveCancelledException(); + } + else { + List> oldList = new ArrayList>(readonlyFiles); + readonlyFiles.clear(); + for (Pair entry : oldList) { + errors = executeSave(entry.first, readonlyFiles, errors); } - if (status.hasReadonlyFiles()) { - ProjectImpl.dropUnableToSaveProjectNotification(myProject, status.getReadonlyFiles()); + if (errors != null) { + CompoundRuntimeException.doThrow(errors); + } + + if (!readonlyFiles.isEmpty()) { + dropUnableToSaveProjectNotification(myProject, getFilesList(readonlyFiles)); throw new SaveCancelledException(); } - else { - List> oldList = new ArrayList>(readonlyFiles); - readonlyFiles.clear(); - for (Pair entry : oldList) { - errors = executeSave(entry.first, readonlyFiles, errors); - } - - if (errors != null) { - CompoundRuntimeException.doThrow(errors); - } - - if (!readonlyFiles.isEmpty()) { - ProjectImpl.dropUnableToSaveProjectNotification(myProject, getFilesList(readonlyFiles)); - throw new SaveCancelledException(); - } - } } return errors; } + private static void dropUnableToSaveProjectNotification(@NotNull Project project, @NotNull VirtualFile[] readOnlyFiles) { + UnableToSaveProjectNotification[] notifications = + NotificationsManager.getNotificationsManager().getNotificationsOfType(UnableToSaveProjectNotification.class, project); + if (notifications.length == 0) { + Notifications.Bus.notify(new UnableToSaveProjectNotification(project, readOnlyFiles), project); + } + else { + notifications[0].myFiles = readOnlyFiles; + } + } + protected void beforeSave(@NotNull List> readonlyFiles) { } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java index 1a0e16dfc640..bdc7fa836577 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.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. @@ -19,7 +19,7 @@ import com.intellij.ide.RecentProjectsManager; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.ide.startup.StartupManagerEx; -import com.intellij.notification.*; +import com.intellij.notification.NotificationsManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathMacros; import com.intellij.openapi.application.ex.ApplicationEx; @@ -54,9 +54,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.impl.FrameTitleBuilder; -import com.intellij.util.Function; import com.intellij.util.TimedReference; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.pico.ConstructorInjectionComponentAdapter; import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; @@ -65,7 +63,6 @@ import org.jetbrains.annotations.Nullable; import org.picocontainer.*; import javax.swing.*; -import javax.swing.event.HyperlinkEvent; import java.io.File; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -520,59 +517,4 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project protected boolean logSlowComponents() { return super.logSlowComponents() || ApplicationInfoImpl.getShadowInstance().isEAP(); } - - public static void dropUnableToSaveProjectNotification(@NotNull final Project project, final VirtualFile[] readOnlyFiles) { - final UnableToSaveProjectNotification[] notifications = - NotificationsManager.getNotificationsManager().getNotificationsOfType(UnableToSaveProjectNotification.class, project); - if (notifications.length == 0) { - Notifications.Bus.notify(new UnableToSaveProjectNotification(project, readOnlyFiles), project); - } - } - - public static class UnableToSaveProjectNotification extends Notification { - private Project myProject; - private final String[] myFileNames; - - private UnableToSaveProjectNotification(@NotNull final Project project, final VirtualFile[] readOnlyFiles) { - super("Project Settings", "Could not save project!", buildMessage(), NotificationType.ERROR, new NotificationListener() { - @Override - public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - final UnableToSaveProjectNotification unableToSaveProjectNotification = (UnableToSaveProjectNotification)notification; - final Project _project = unableToSaveProjectNotification.getProject(); - notification.expire(); - - if (_project != null && !_project.isDisposed()) { - _project.save(); - } - } - }); - - myProject = project; - myFileNames = ContainerUtil.map(readOnlyFiles, new Function() { - @Override - public String fun(VirtualFile file) { - return file.getPresentableUrl(); - } - }, new String[readOnlyFiles.length]); - } - - public String[] getFileNames() { - return myFileNames; - } - - private static String buildMessage() { - return "

Unable to save project files. Please ensure project files are writable and you have permissions to modify them." + - " Try to save project again.

"; - } - - public Project getProject() { - return myProject; - } - - @Override - public void expire() { - myProject = null; - super.expire(); - } - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index 401d3e5f9c09..f51d5cac1dce 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -23,6 +23,9 @@ import com.intellij.ide.RecentProjectsManager; import com.intellij.ide.impl.ProjectUtil; import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.startup.impl.StartupManagerImpl; +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationListener; +import com.intellij.notification.NotificationType; import com.intellij.notification.NotificationsManager; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.*; @@ -42,7 +45,6 @@ import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileEvent; @@ -67,6 +69,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +import javax.swing.event.HyperlinkEvent; import java.io.File; import java.io.IOException; import java.util.*; @@ -976,17 +979,30 @@ public class ProjectManagerImpl extends ProjectManagerEx implements PersistentSt return true; } - private static boolean ensureCouldCloseIfUnableToSave(@NotNull final Project project) { - final ProjectImpl.UnableToSaveProjectNotification[] notifications = - NotificationsManager.getNotificationsManager().getNotificationsOfType(ProjectImpl.UnableToSaveProjectNotification.class, project); - if (notifications.length == 0) return true; + private static boolean ensureCouldCloseIfUnableToSave(@NotNull Project project) { + UnableToSaveProjectNotification[] notifications = + NotificationsManager.getNotificationsManager().getNotificationsOfType(UnableToSaveProjectNotification.class, project); + if (notifications.length == 0) { + return true; + } - final String fileNames = StringUtil.join(notifications[0].getFileNames(), "\n"); + StringBuilder message = new StringBuilder(); + message.append(String.format("%s was unable to save some project files,\nare you sure you want to close this project anyway?", + ApplicationNamesInfo.getInstance().getProductName())); - final String msg = String.format("%s was unable to save some project files,\nare you sure you want to close this project anyway?", - ApplicationNamesInfo.getInstance().getProductName()); - return Messages.showDialog(project, msg, "Unsaved Project", "Read-only files:\n\n" + fileNames, new String[]{"Yes", "No"}, 0, 1, - Messages.getWarningIcon()) == 0; + message.append("\n\nRead-only files:\n"); + int count = 0; + VirtualFile[] files = notifications[0].myFiles; + for (VirtualFile file : files) { + if (count == 10) { + message.append('\n').append("and ").append(files.length - count).append(" more").append('\n'); + } + else { + message.append(file.getPath()).append('\n'); + count++; + } + } + return Messages.showYesNoDialog(project, message.toString(), "Unsaved Project", Messages.getWarningIcon()) == Messages.YES; } @@ -1039,4 +1055,34 @@ public class ProjectManagerImpl extends ProjectManagerEx implements PersistentSt public String getPresentableName() { return ProjectBundle.message("project.default.settings"); } + + public static class UnableToSaveProjectNotification extends Notification { + private Project myProject; + public VirtualFile[] myFiles; + + public UnableToSaveProjectNotification(@NotNull final Project project, final VirtualFile[] readOnlyFiles) { + super("Project Settings", "Could not save project", "Unable to save project files. Please ensure project files are writable and you have permissions to modify them." + + " Try to save project again.", NotificationType.ERROR, new NotificationListener() { + @Override + public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { + final UnableToSaveProjectNotification unableToSaveProjectNotification = (UnableToSaveProjectNotification)notification; + final Project _project = unableToSaveProjectNotification.myProject; + notification.expire(); + + if (_project != null && !_project.isDisposed()) { + _project.save(); + } + } + }); + + myProject = project; + myFiles = readOnlyFiles; + } + + @Override + public void expire() { + myProject = null; + super.expire(); + } + } }