Răsfoiți Sursa

Autosave unsaved files on close

CPKreuz 1 an în urmă
părinte
comite
f1d4aac5a4

+ 2 - 0
src/PixiEditor.Extensions/Common/UserPreferences/PreferencesConstants.cs

@@ -17,4 +17,6 @@ public static class PreferencesConstants
     
     public const string AutosavePeriodMinutes = nameof(AutosavePeriodMinutes);
     public const double AutosavePeriodDefault = 3;
+
+    public const string UnsavedNextSessionFiles = nameof(UnsavedNextSessionFiles);
 }

+ 10 - 1
src/PixiEditor/ViewModels/SubViewModels/Document/AutosaveDocumentViewModel.cs

@@ -115,6 +115,9 @@ internal class AutosaveDocumentViewModel : NotifyableObject
         AutosavePeriodChanged(preferences.GetPreference(PreferencesConstants.AutosavePeriodMinutes, PreferencesConstants.AutosavePeriodDefault), documentEnabled);
     }
 
+    public static bool AutosavingEnabled =>
+        (int)IPreferences.Current.GetPreference(PreferencesConstants.AutosavePeriodMinutes, PreferencesConstants.AutosavePeriodDefault) != -1;
+
     public void HintFinishedAction()
     {
         if (!saveAfterNextFinish)
@@ -155,7 +158,7 @@ internal class AutosaveDocumentViewModel : NotifyableObject
         UpdateMainMenuTextSave(new LocalizedString("AUTOSAVE_SAVING_IN", adjusted.Minutes.ToString(), minute), ClockIcon, InactiveBrush, false);
     }
 
-    private void TryAutosave()
+    public void TryAutosave()
     {
         if (Document.UpdateableChangeActive)
         {
@@ -318,6 +321,12 @@ internal class AutosaveDocumentViewModel : NotifyableObject
         }
     }
 
+    public void SetTempFileGuiAndLastSavedPath(Guid guid, string lastSavedPath)
+    {
+        tempGuid = guid;
+        LastSavedPath = lastSavedPath;
+    }
+
     private void UpdateMainMenuTextSave(LocalizedString text, string iconText, Brush brush, bool pulse)
     {
         Application.Current.Dispatcher.Invoke(() =>

+ 41 - 15
src/PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -22,6 +22,7 @@ using PixiEditor.Models.Localization;
 using PixiEditor.Parser;
 using PixiEditor.ViewModels.SubViewModels.Document;
 using PixiEditor.Views.Dialogs;
+using Path = System.IO.Path;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main;
 
@@ -98,11 +99,17 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
     {
         List<string> args = StartupArgs.Args;
         string file = args.FirstOrDefault(x => Importer.IsSupportedFile(x) && File.Exists(x));
+
+        if (!args.Contains("--crash"))
+        {
+            ReopenUnsavedFiles();
+        }
+        
         if (file != null)
         {
             OpenFromPath(file);
         }
-        else if ((Owner.DocumentManagerSubViewModel.Documents.Count == 0 && !args.Contains("--crash")) && !args.Contains("--openedInExisting"))
+        else if ((!args.Contains("--crash")) && !args.Contains("--openedInExisting"))
         {
             if (IPreferences.Current.GetPreference("ShowStartupWindow", true))
             {
@@ -111,6 +118,25 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         }
     }
 
+    private void ReopenUnsavedFiles()
+    {
+        var preferences = Owner.Preferences;
+        var files = preferences.GetLocalPreference<string[]>(PreferencesConstants.UnsavedNextSessionFiles);
+
+        if (files == null)
+            return;
+        
+        foreach (string file in files)
+        {
+            string guidString = Path.GetFileNameWithoutExtension(file)["autosave-".Length..];
+            var document = OpenFromPath(file, false);
+            
+            document.AutosaveViewModel.SetTempFileGuiAndLastSavedPath(Guid.Parse(guidString), file);
+        }
+        
+        preferences.UpdateLocalPreference(PreferencesConstants.UnsavedNextSessionFiles, Array.Empty<string>());
+    }
+
     [Command.Internal("PixiEditor.File.OpenRecent")]
     public void OpenRecent(object parameter)
     {
@@ -176,21 +202,16 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
     /// <summary>
     /// Tries to open the passed file if it isn't already open
     /// </summary>
-    public void OpenFromPath(string path, bool associatePath = true)
+    public DocumentViewModel OpenFromPath(string path, bool associatePath = true)
     {
         if (MakeExistingDocumentActiveIfOpened(path))
-            return;
+            return null;
 
         try
         {
-            if (path.EndsWith(".pixi"))
-            {
-                OpenDotPixi(path, associatePath);
-            }
-            else
-            {
-                OpenRegularImage(path, associatePath);
-            }
+            return path.EndsWith(".pixi")
+                ? OpenDotPixi(path, associatePath)
+                : OpenRegularImage(path, associatePath);
         }
         catch (RecoverableException ex)
         {
@@ -200,16 +221,20 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         {
             NoticeDialog.Show("OLD_FILE_FORMAT_DESCRIPTION", "OLD_FILE_FORMAT");
         }
+
+        return null;
     }
 
     /// <summary>
     /// Opens a .pixi file from path, creates a document from it, and adds it to the system
     /// </summary>
-    private void OpenDotPixi(string path, bool associatePath = true)
+    private DocumentViewModel OpenDotPixi(string path, bool associatePath = true)
     {
-        DocumentViewModel document = Importer.ImportDocument(path, associatePath);
+        var document = Importer.ImportDocument(path, associatePath);
         AddDocumentViewModelToTheSystem(document);
         AddRecentlyOpened(document.FullFilePath);
+
+        return document;
     }
 
     /// <summary>
@@ -225,11 +250,11 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
     /// <summary>
     /// Opens a regular image file from path, creates a document from it, and adds it to the system.
     /// </summary>
-    private void OpenRegularImage(string path, bool associatePath)
+    private DocumentViewModel OpenRegularImage(string path, bool associatePath)
     {
         var image = Importer.ImportImage(path, VecI.NegativeOne);
 
-        if (image == null) return;
+        if (image == null) return null;
 
         var doc = NewDocument(b => b
             .WithSize(image.Size)
@@ -244,6 +269,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         }
 
         AddRecentlyOpened(path);
+        return doc;
     }
 
     /// <summary>

+ 22 - 0
src/PixiEditor/ViewModels/ViewModelMain.cs

@@ -12,6 +12,7 @@ using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Dialogs;
+using PixiEditor.Models.DocumentModels.Public;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Localization;
@@ -202,6 +203,7 @@ internal class ViewModelMain : ViewModelBase
             throw new ArgumentException();
         }
 
+        AutosaveUnsavedForNextSession();
         ((CancelEventArgs)property).Cancel = !DisposeAllDocumentsWithSaveConfirmation();
     }
 
@@ -247,6 +249,26 @@ internal class ViewModelMain : ViewModelBase
         return true;
     }
 
+    private void AutosaveUnsavedForNextSession()
+    {
+        if (!AutosaveDocumentViewModel.AutosavingEnabled)
+        {
+            return;
+        }
+        
+        var list = new List<string>();
+        foreach (var document in DocumentManagerSubViewModel.Documents.Where(x => x.FullFilePath == null))
+        {
+            document.AutosaveViewModel.TryAutosave();
+            if (document.AutosaveViewModel.LastSavedPath != null)
+            {
+                list.Add(document.AutosaveViewModel.LastSavedPath);
+            }
+        }
+        
+        Preferences.UpdateLocalPreference(PreferencesConstants.UnsavedNextSessionFiles, list);
+    }
+
     /// <summary>
     /// Disposes the active document after showing the unsaved changes confirmation dialog.
     /// </summary>