diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj
index 23a52fcd..54cd5111 100644
--- a/Source/Core/Builder.csproj
+++ b/Source/Core/Builder.csproj
@@ -241,6 +241,7 @@
+
diff --git a/Source/Core/BuilderMono.csproj b/Source/Core/BuilderMono.csproj
index cd64feac..52d17bcc 100644
--- a/Source/Core/BuilderMono.csproj
+++ b/Source/Core/BuilderMono.csproj
@@ -233,6 +233,7 @@
+
diff --git a/Source/Core/Config/ProgramConfiguration.cs b/Source/Core/Config/ProgramConfiguration.cs
index 839cd548..bbde2536 100755
--- a/Source/Core/Config/ProgramConfiguration.cs
+++ b/Source/Core/Config/ProgramConfiguration.cs
@@ -160,6 +160,11 @@ namespace CodeImp.DoomBuilder.Config
private int defaultthingtype = 1;
private double defaultthingangle;
private List defaultthingflags;
+
+ // Autosave
+ private bool autosave;
+ private int autosavecount;
+ private int autosaveinterval;
#endregion
@@ -298,6 +303,11 @@ namespace CodeImp.DoomBuilder.Config
public int DefaultThingType { get { return defaultthingtype; } set { defaultthingtype = value; } }
public double DefaultThingAngle { get { return defaultthingangle; } set { defaultthingangle = value; } }
+ // Autosave
+ public bool Autosave { get { return autosave; } internal set { autosave = value; } }
+ public int AutosaveCount { get { return autosavecount; } internal set { autosavecount = value; } }
+ public int AutosaveInterval { get { return autosaveinterval; } internal set { autosaveinterval = value; } }
+
#endregion
#region ================== Constructor / Disposer
@@ -455,6 +465,11 @@ namespace CodeImp.DoomBuilder.Config
}
}
+ // Autosave
+ autosave = cfg.ReadSetting("autosave", true);
+ autosavecount = cfg.ReadSetting("autosavecount", 5);
+ autosaveinterval = cfg.ReadSetting("autosaveinterval", 5);
+
// Success
return true;
}
@@ -584,6 +599,11 @@ namespace CodeImp.DoomBuilder.Config
for (int i = 0; i < 16; i++)
cfg.WriteSetting("colordialogcustomcolors.color" + i, colordialogcustomcolors[i]);
+ // Autosave
+ cfg.WriteSetting("autosave", autosave);
+ cfg.WriteSetting("autosavecount", autosavecount);
+ cfg.WriteSetting("autosaveinterval", autosaveinterval);
+
// Save settings configuration
General.WriteLogLine("Saving program configuration to \"" + filepathname + "\"...");
cfg.SaveConfiguration(filepathname);
diff --git a/Source/Core/Editing/EditMode.cs b/Source/Core/Editing/EditMode.cs
index a2c4aa56..090325e6 100755
--- a/Source/Core/Editing/EditMode.cs
+++ b/Source/Core/Editing/EditMode.cs
@@ -264,6 +264,9 @@ namespace CodeImp.DoomBuilder.Editing
// This should be called by global actions (i.e. that are not part of an editing mode) when they changed map elements,
// so that the mode can react to it (for example by rebuilding a blockmap)
public virtual void OnMapElementsChanged() { }
+
+ public virtual bool OnAutoSaveBegin() { return attributes.Volatile ? false : true; } // Called before autosave is done. Returns false if autosave should not be done. By default volatile modes prevent autosave
+ public virtual void OnAutoSaveEnd() { }
#endregion
}
diff --git a/Source/Core/General/AutoSaver.cs b/Source/Core/General/AutoSaver.cs
new file mode 100644
index 00000000..953c201e
--- /dev/null
+++ b/Source/Core/General/AutoSaver.cs
@@ -0,0 +1,120 @@
+#region ================== Copyright (c) 2023 Boris Iwanski
+
+/*
+ * This program is free software: you can redistribute it and/or modify
+ *
+ * it under the terms of the GNU General Public License as published by
+ *
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.If not, see.
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeImp.DoomBuilder
+{
+ internal enum AutosaveResult
+ {
+ Success,
+ Error,
+ NoFileName
+ }
+
+ internal class AutoSaver
+ {
+ private static long lasttime;
+ private static System.Windows.Forms.Timer timer;
+
+ ///
+ /// Initialized and starts the autosave timer.
+ ///
+ internal void InitializeTimer()
+ {
+ if(timer != null)
+ {
+ timer.Tick -= TryAutosave;
+ timer.Dispose();
+ timer = null;
+ }
+
+ if (General.Settings.Autosave)
+ {
+ lasttime = Clock.CurrentTime;
+ timer = new System.Windows.Forms.Timer() { Interval = 1000 };
+ timer.Tick += TryAutosave;
+ timer.Enabled = true;
+ }
+ }
+
+ ///
+ /// Stops the autosave timer.
+ ///
+ internal void StopTimer()
+ {
+ if (timer != null) timer.Enabled = false;
+ }
+
+ ///
+ /// Resets the autosave timer to the current time.
+ ///
+ internal void ResetTimer()
+ {
+ lasttime = Clock.CurrentTime;
+ }
+
+ ///
+ /// Makes the autosave timer aware of a clock reset, so that the interval until the next autosave is unaffected.
+ ///
+ internal void BeforeClockReset()
+ {
+ lasttime = -(Clock.CurrentTime - lasttime);
+ }
+
+ ///
+ /// Tries to perform the autosave.
+ ///
+ /// The sender
+ /// The event arguments
+ private static void TryAutosave(object sender, EventArgs args)
+ {
+ if (Clock.CurrentTime > lasttime + General.Settings.AutosaveInterval * 60 * 1000 && General.Map != null && General.Map.Map != null && General.Map.Map.IsSafeToAccess && General.Map.IsChanged)
+ {
+ // Check if the current editing mode prevents autosaving. If it does return without setting the time,
+ // so that autosaving will be retried ASAP
+ if (!General.Editing.Mode.OnAutoSaveBegin())
+ return;
+
+ lasttime = Clock.CurrentTime;
+
+ long start = Clock.CurrentTime;
+ AutosaveResult success = General.Map.AutoSave();
+ long duration = Clock.CurrentTime - start;
+
+ // Show a toast appropriate for the result of the autosave
+ if (success == AutosaveResult.Success)
+ General.ToastManager.ShowToast("autosave", ToastType.INFO, "Autosave", $"Autosave completed successfully in {duration} ms.");
+ else if (success == AutosaveResult.Error)
+ General.ToastManager.ShowToast("autosave", ToastType.ERROR, "Autosave", "Autosave failed.");
+ else if (success == AutosaveResult.NoFileName)
+ General.ToastManager.ShowToast("autosave", ToastType.WARNING, "Autosave", "Could not autosave because this is a new WAD that wasn't saved yet.");
+ }
+ }
+ }
+}
diff --git a/Source/Core/General/General.cs b/Source/Core/General/General.cs
index 225c5ca2..3ceaf665 100755
--- a/Source/Core/General/General.cs
+++ b/Source/Core/General/General.cs
@@ -239,6 +239,9 @@ namespace CodeImp.DoomBuilder
// Toasts
private static ToastManager toastmanager;
+ // Autosaving
+ private static AutoSaver autosaver;
+
#endregion
#region ================== Properties
@@ -284,6 +287,7 @@ namespace CodeImp.DoomBuilder
public static ErrorLogger ErrorLogger { get { return errorlogger; } }
public static string CommitHash { get { return commithash; } } //mxd
public static ToastManager ToastManager { get => toastmanager; }
+ internal static AutoSaver AutoSaver { get => autosaver; }
#endregion
@@ -808,6 +812,9 @@ namespace CodeImp.DoomBuilder
if(General.Settings.CheckForUpdates) UpdateChecker.PerformCheck(false);
#endif
+ // Prepare autosaving
+ autosaver = new AutoSaver();
+
// Run application from the main window
Application.Run(mainwindow);
}
@@ -821,6 +828,7 @@ namespace CodeImp.DoomBuilder
private static void RegisterToasts()
{
toastmanager.RegisterToast("resourcewarningsanderrors", "Resource warnings and errors", "When there are errors or warning while (re)loading the resources");
+ toastmanager.RegisterToast("autosave", "Autosave", "Notifications related to autosaving");
}
// This parses the command line arguments
@@ -1160,7 +1168,7 @@ namespace CodeImp.DoomBuilder
//mxd. Also reset the clock...
MainWindow.ResetClock();
-
+
Cursor.Current = Cursors.Default;
}
}
diff --git a/Source/Core/General/MapManager.cs b/Source/Core/General/MapManager.cs
index 10c11c8f..47cc4ba6 100755
--- a/Source/Core/General/MapManager.cs
+++ b/Source/Core/General/MapManager.cs
@@ -177,6 +177,9 @@ namespace CodeImp.DoomBuilder
// Let the plugins know
General.Plugins.OnMapCloseBegin();
+ // Stop autosaving
+ General.AutoSaver.StopTimer();
+
// Stop processing
General.MainWindow.StopProcessing();
@@ -343,6 +346,9 @@ namespace CodeImp.DoomBuilder
renderer2d.SetViewMode((ViewMode)General.Settings.DefaultViewMode);
General.Settings.SetDefaultThingFlags(config.DefaultThingFlags);
+ // Autosaver
+ General.AutoSaver.InitializeTimer();
+
// Success
this.changed = false;
this.maploading = false; //mxd
@@ -462,6 +468,9 @@ namespace CodeImp.DoomBuilder
// Center map in screen
//if(General.Editing.Mode is ClassicMode) (General.Editing.Mode as ClassicMode).CenterInScreen();
+ // Autosaver
+ General.AutoSaver.InitializeTimer();
+
// Success
this.changed = maprestored; //mxd
this.maploading = false; //mxd
@@ -575,6 +584,9 @@ namespace CodeImp.DoomBuilder
}
}
+ // Autosaver
+ General.AutoSaver.InitializeTimer();
+
// Success
this.changed = maprestored;
this.maploading = false;
@@ -665,6 +677,26 @@ namespace CodeImp.DoomBuilder
return result;
}
+ ///
+ /// Autosaves the map.
+ ///
+ /// The result of the autosave
+ internal AutosaveResult AutoSave()
+ {
+ // If the map doesn't exist on a medium we can't make autosaves
+ if (string.IsNullOrWhiteSpace(filepathname))
+ return AutosaveResult.NoFileName;
+
+ // Generat the file name. This is the current file name, a dot, and the map slot, for example
+ // cacowardwinner.wad.MAP01
+ // the SaveMap method will add an ".autosaveX" due to the save purpose being Autosave
+ string autosavefilename = filepathname + "." + options.CurrentName;
+ General.Plugins.OnMapSaveBegin(SavePurpose.Autosave);
+ bool result = SaveMap(autosavefilename, SavePurpose.Autosave);
+ General.Plugins.OnMapSaveEnd(SavePurpose.Autosave);
+ return result ? AutosaveResult.Success : AutosaveResult.Error;
+ }
+
///
/// This writes the map structures to the temporary file.
///
@@ -755,6 +787,10 @@ namespace CodeImp.DoomBuilder
// Initializes for an existing map
internal bool SaveMap(string newfilepathname, SavePurpose purpose)
{
+ // Add the autosave suffix. As all existing autosave will be shifted up this is static
+ if (purpose == SavePurpose.Autosave)
+ newfilepathname += ".autosave1";
+
string settingsfile;
WAD targetwad = null;
bool includenodes;
@@ -805,14 +841,22 @@ namespace CodeImp.DoomBuilder
// Write the current map structures to the temp file
if(!WriteMapToTempFile()) return false;
- // Get the corresponding nodebuilder
- string nodebuildername = (purpose == SavePurpose.Testing) ? configinfo.NodebuilderTest : configinfo.NodebuilderSave;
+ // Only build nodes when not autosaving
+ if (purpose != SavePurpose.Autosave)
+ {
+ // Get the corresponding nodebuilder
+ string nodebuildername = (purpose == SavePurpose.Testing) ? configinfo.NodebuilderTest : configinfo.NodebuilderSave;
- // Build the nodes
- StatusInfo oldstatus = General.MainWindow.Status;
- General.MainWindow.DisplayStatus(StatusType.Busy, "Building map nodes...");
- includenodes = (!string.IsNullOrEmpty(nodebuildername) && BuildNodes(nodebuildername, true));
- General.MainWindow.DisplayStatus(oldstatus);
+ // Build the nodes
+ StatusInfo oldstatus = General.MainWindow.Status;
+ General.MainWindow.DisplayStatus(StatusType.Busy, "Building map nodes...");
+ includenodes = (!string.IsNullOrEmpty(nodebuildername) && BuildNodes(nodebuildername, true));
+ General.MainWindow.DisplayStatus(oldstatus);
+ }
+ else
+ {
+ includenodes = false;
+ }
//mxd. Compress temp file...
tempwadreader.WadFile.Compress();
@@ -928,16 +972,34 @@ namespace CodeImp.DoomBuilder
}
}
- // Backup existing file, if any
- if(File.Exists(newfilepathname + ".backup3")) File.Delete(newfilepathname + ".backup3");
- if(File.Exists(newfilepathname + ".backup2")) File.Move(newfilepathname + ".backup2", newfilepathname + ".backup3");
- if(File.Exists(newfilepathname + ".backup1")) File.Move(newfilepathname + ".backup1", newfilepathname + ".backup2");
- File.Copy(newfilepathname, newfilepathname + ".backup1");
+ if (purpose == SavePurpose.Autosave)
+ {
+ string autosavefilepathname = Path.Combine(Path.GetDirectoryName(newfilepathname), Path.GetFileNameWithoutExtension(newfilepathname));
+
+ // Delete the last autosave if it exists
+ if (File.Exists($"{autosavefilepathname}.autosave{General.Settings.AutosaveCount}"))
+ File.Delete($"{autosavefilepathname}.autosave{General.Settings.AutosaveCount}");
+
+ // Move all other autosaves up by one
+ for (int i = General.Settings.AutosaveCount-1; i > 0; i--)
+ {
+ if (File.Exists($"{autosavefilepathname}.autosave{i}"))
+ File.Move($"{autosavefilepathname}.autosave{i}", $"{autosavefilepathname}.autosave{i + 1}");
+ }
+ }
+ else
+ {
+ // Backup existing file, if any
+ if (File.Exists(newfilepathname + ".backup3")) File.Delete(newfilepathname + ".backup3");
+ if (File.Exists(newfilepathname + ".backup2")) File.Move(newfilepathname + ".backup2", newfilepathname + ".backup3");
+ if (File.Exists(newfilepathname + ".backup1")) File.Move(newfilepathname + ".backup1", newfilepathname + ".backup2");
+ File.Copy(newfilepathname, newfilepathname + ".backup1");
+ }
}
// Except when saving INTO another file,
// kill the target file if it is different from source file
- if((purpose != SavePurpose.IntoFile) && (newfilepathname != filepathname))
+ if ((purpose != SavePurpose.IntoFile) && (newfilepathname != filepathname))
{
// Kill target file
if(File.Exists(newfilepathname)) File.Delete(newfilepathname);
@@ -1043,8 +1105,8 @@ namespace CodeImp.DoomBuilder
// Resume data resources
data.Resume();
- // Not saved for testing purpose?
- if(purpose != SavePurpose.Testing)
+ // Not saved for testing or autosave purpose?
+ if(purpose != SavePurpose.Testing && purpose != SavePurpose.Autosave)
{
// Saved in a different file?
if(newfilepathname != filepathname)
@@ -1069,6 +1131,9 @@ namespace CodeImp.DoomBuilder
General.ErrorLogger.Add(ErrorType.Warning, "Could not write the map settings configuration file. " + e.GetType().Name + ": " + e.Message);
}
+ // Autosaver
+ General.AutoSaver.InitializeTimer();
+
// Changes saved
changed = false;
scriptschanged = false;
diff --git a/Source/Core/General/SavePurpose.cs b/Source/Core/General/SavePurpose.cs
index b82fdee9..a68886d0 100755
--- a/Source/Core/General/SavePurpose.cs
+++ b/Source/Core/General/SavePurpose.cs
@@ -25,7 +25,8 @@ namespace CodeImp.DoomBuilder
Normal = 0,
AsNewFile = 1,
IntoFile = 2,
- Testing = 3
+ Testing = 3,
+ Autosave = 4
}
}
diff --git a/Source/Core/Windows/MainForm.cs b/Source/Core/Windows/MainForm.cs
index 2b403274..6ddad9b1 100755
--- a/Source/Core/Windows/MainForm.cs
+++ b/Source/Core/Windows/MainForm.cs
@@ -3616,6 +3616,9 @@ namespace CodeImp.DoomBuilder.Windows
[BeginAction("preferences")]
internal void ShowPreferences()
{
+ // Remember the old autostave state, so that we can enable/disable it
+ bool oldautosavestate = General.Settings.Autosave;
+
// Show preferences dialog
PreferencesForm prefform = new PreferencesForm();
if(prefform.ShowDialog(this) == DialogResult.OK)
@@ -3638,6 +3641,15 @@ namespace CodeImp.DoomBuilder.Windows
General.Map.Graphics.SetupSettings();
General.Map.UpdateConfiguration();
if(prefform.ReloadResources) General.Actions.InvokeAction("builder_reloadresources");
+
+ // Autosave stats was changed, so we have to enable or disable it
+ if(oldautosavestate != General.Settings.Autosave)
+ {
+ if (General.Settings.Autosave)
+ General.AutoSaver.InitializeTimer();
+ else
+ General.AutoSaver.StopTimer();
+ }
}
// Redraw display
@@ -4646,6 +4658,9 @@ namespace CodeImp.DoomBuilder.Windows
//mxd
internal void ResetClock()
{
+ // Let the autosaver know that the clock is about to be reset
+ General.AutoSaver.BeforeClockReset();
+
Clock.Reset();
lastupdatetime = 0;
diff --git a/Source/Core/Windows/PreferencesForm.Designer.cs b/Source/Core/Windows/PreferencesForm.Designer.cs
index 787bf2df..e907c57c 100755
--- a/Source/Core/Windows/PreferencesForm.Designer.cs
+++ b/Source/Core/Windows/PreferencesForm.Designer.cs
@@ -215,6 +215,18 @@ namespace CodeImp.DoomBuilder.Windows
this.tabpasting = new System.Windows.Forms.TabPage();
this.label16 = new System.Windows.Forms.Label();
this.pasteoptions = new CodeImp.DoomBuilder.Controls.PasteOptionsControl();
+ this.tabrecovery = new System.Windows.Forms.TabPage();
+ this.autosavegroupbox = new System.Windows.Forms.GroupBox();
+ this.autosavedisabledwarning = new System.Windows.Forms.Panel();
+ this.label34 = new System.Windows.Forms.Label();
+ this.pictureBox1 = new System.Windows.Forms.PictureBox();
+ this.autosavecountlabel = new System.Windows.Forms.Label();
+ this.autosavecount = new CodeImp.DoomBuilder.Controls.TransparentTrackBar();
+ this.label21 = new System.Windows.Forms.Label();
+ this.autosaveintervallabel = new System.Windows.Forms.Label();
+ this.autosaveinterval = new CodeImp.DoomBuilder.Controls.TransparentTrackBar();
+ this.label20 = new System.Windows.Forms.Label();
+ this.autosave = new System.Windows.Forms.CheckBox();
this.tabtoasts = new System.Windows.Forms.TabPage();
this.groupBox10 = new System.Windows.Forms.GroupBox();
this.lvToastActions = new System.Windows.Forms.ListView();
@@ -273,6 +285,12 @@ namespace CodeImp.DoomBuilder.Windows
this.groupBox6.SuspendLayout();
this.previewgroup.SuspendLayout();
this.tabpasting.SuspendLayout();
+ this.tabrecovery.SuspendLayout();
+ this.autosavegroupbox.SuspendLayout();
+ this.autosavedisabledwarning.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.autosavecount)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.autosaveinterval)).BeginInit();
this.tabtoasts.SuspendLayout();
this.groupBox10.SuspendLayout();
this.gbToastPosition.SuspendLayout();
@@ -809,6 +827,7 @@ namespace CodeImp.DoomBuilder.Windows
this.tabs.Controls.Add(this.tabcolors);
this.tabs.Controls.Add(this.tabscripteditor);
this.tabs.Controls.Add(this.tabpasting);
+ this.tabs.Controls.Add(this.tabrecovery);
this.tabs.Controls.Add(this.tabtoasts);
this.tabs.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.tabs.Location = new System.Drawing.Point(11, 13);
@@ -2448,6 +2467,139 @@ namespace CodeImp.DoomBuilder.Windows
this.pasteoptions.Size = new System.Drawing.Size(666, 427);
this.pasteoptions.TabIndex = 0;
//
+ // tabrecovery
+ //
+ this.tabrecovery.Controls.Add(this.autosavegroupbox);
+ this.tabrecovery.Location = new System.Drawing.Point(4, 22);
+ this.tabrecovery.Name = "tabrecovery";
+ this.tabrecovery.Size = new System.Drawing.Size(680, 526);
+ this.tabrecovery.TabIndex = 6;
+ this.tabrecovery.Text = "Recovery";
+ this.tabrecovery.UseVisualStyleBackColor = true;
+ //
+ // autosavegroupbox
+ //
+ this.autosavegroupbox.Controls.Add(this.autosavedisabledwarning);
+ this.autosavegroupbox.Controls.Add(this.autosavecountlabel);
+ this.autosavegroupbox.Controls.Add(this.autosavecount);
+ this.autosavegroupbox.Controls.Add(this.label21);
+ this.autosavegroupbox.Controls.Add(this.autosaveintervallabel);
+ this.autosavegroupbox.Controls.Add(this.autosaveinterval);
+ this.autosavegroupbox.Controls.Add(this.label20);
+ this.autosavegroupbox.Controls.Add(this.autosave);
+ this.autosavegroupbox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.autosavegroupbox.Location = new System.Drawing.Point(8, 8);
+ this.autosavegroupbox.Name = "autosavegroupbox";
+ this.autosavegroupbox.Size = new System.Drawing.Size(666, 147);
+ this.autosavegroupbox.TabIndex = 0;
+ this.autosavegroupbox.TabStop = false;
+ this.autosavegroupbox.Text = "Autosave";
+ //
+ // autosavedisabledwarning
+ //
+ this.autosavedisabledwarning.BackColor = System.Drawing.SystemColors.Info;
+ this.autosavedisabledwarning.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+ this.autosavedisabledwarning.Controls.Add(this.label34);
+ this.autosavedisabledwarning.Controls.Add(this.pictureBox1);
+ this.autosavedisabledwarning.ForeColor = System.Drawing.SystemColors.InfoText;
+ this.autosavedisabledwarning.Location = new System.Drawing.Point(420, 12);
+ this.autosavedisabledwarning.Name = "autosavedisabledwarning";
+ this.autosavedisabledwarning.Size = new System.Drawing.Size(240, 24);
+ this.autosavedisabledwarning.TabIndex = 7;
+ //
+ // label34
+ //
+ this.label34.AutoSize = true;
+ this.label34.Location = new System.Drawing.Point(25, 4);
+ this.label34.Name = "label34";
+ this.label34.Size = new System.Drawing.Size(214, 13);
+ this.label34.TabIndex = 1;
+ this.label34.Text = "It is not recommended to disable autosaves!";
+ //
+ // pictureBox1
+ //
+ this.pictureBox1.Image = global::CodeImp.DoomBuilder.Properties.Resources.Warning;
+ this.pictureBox1.Location = new System.Drawing.Point(3, 3);
+ this.pictureBox1.Name = "pictureBox1";
+ this.pictureBox1.Size = new System.Drawing.Size(16, 16);
+ this.pictureBox1.TabIndex = 0;
+ this.pictureBox1.TabStop = false;
+ //
+ // autosavecountlabel
+ //
+ this.autosavecountlabel.AutoSize = true;
+ this.autosavecountlabel.Location = new System.Drawing.Point(285, 104);
+ this.autosavecountlabel.Name = "autosavecountlabel";
+ this.autosavecountlabel.Size = new System.Drawing.Size(13, 13);
+ this.autosavecountlabel.TabIndex = 6;
+ this.autosavecountlabel.Text = "5";
+ //
+ // autosavecount
+ //
+ this.autosavecount.BackColor = System.Drawing.Color.Transparent;
+ this.autosavecount.Location = new System.Drawing.Point(125, 92);
+ this.autosavecount.Maximum = 50;
+ this.autosavecount.Minimum = 1;
+ this.autosavecount.Name = "autosavecount";
+ this.autosavecount.Size = new System.Drawing.Size(154, 45);
+ this.autosavecount.TabIndex = 5;
+ this.autosavecount.TickFrequency = 5;
+ this.autosavecount.TickStyle = System.Windows.Forms.TickStyle.TopLeft;
+ this.autosavecount.Value = 1;
+ this.autosavecount.ValueChanged += new System.EventHandler(this.autosavecount_ValueChanged);
+ //
+ // label21
+ //
+ this.label21.AutoSize = true;
+ this.label21.Location = new System.Drawing.Point(8, 104);
+ this.label21.Name = "label21";
+ this.label21.Size = new System.Drawing.Size(111, 13);
+ this.label21.TabIndex = 4;
+ this.label21.Text = "Number of autosaves:";
+ //
+ // autosaveintervallabel
+ //
+ this.autosaveintervallabel.AutoSize = true;
+ this.autosaveintervallabel.Location = new System.Drawing.Point(285, 53);
+ this.autosaveintervallabel.Name = "autosaveintervallabel";
+ this.autosaveintervallabel.Size = new System.Drawing.Size(52, 13);
+ this.autosaveintervallabel.TabIndex = 3;
+ this.autosaveintervallabel.Text = "5 minutes";
+ //
+ // autosaveinterval
+ //
+ this.autosaveinterval.BackColor = System.Drawing.Color.Transparent;
+ this.autosaveinterval.Location = new System.Drawing.Point(125, 41);
+ this.autosaveinterval.Maximum = 60;
+ this.autosaveinterval.Minimum = 1;
+ this.autosaveinterval.Name = "autosaveinterval";
+ this.autosaveinterval.Size = new System.Drawing.Size(154, 45);
+ this.autosaveinterval.TabIndex = 2;
+ this.autosaveinterval.TickFrequency = 5;
+ this.autosaveinterval.TickStyle = System.Windows.Forms.TickStyle.TopLeft;
+ this.autosaveinterval.Value = 1;
+ this.autosaveinterval.ValueChanged += new System.EventHandler(this.autosaveinterval_ValueChanged);
+ //
+ // label20
+ //
+ this.label20.AutoSize = true;
+ this.label20.Location = new System.Drawing.Point(27, 53);
+ this.label20.Name = "label20";
+ this.label20.Size = new System.Drawing.Size(92, 13);
+ this.label20.TabIndex = 1;
+ this.label20.Text = "Autosave interval:";
+ //
+ // autosave
+ //
+ this.autosave.AutoSize = true;
+ this.autosave.Location = new System.Drawing.Point(8, 19);
+ this.autosave.Name = "autosave";
+ this.autosave.Size = new System.Drawing.Size(107, 17);
+ this.autosave.TabIndex = 0;
+ this.autosave.Text = "Enable Autosave";
+ this.autosave.UseVisualStyleBackColor = true;
+ this.autosave.CheckedChanged += new System.EventHandler(this.autosave_CheckedChanged);
+ //
// tabtoasts
//
this.tabtoasts.Controls.Add(this.groupBox10);
@@ -2675,6 +2827,14 @@ namespace CodeImp.DoomBuilder.Windows
this.groupBox6.PerformLayout();
this.previewgroup.ResumeLayout(false);
this.tabpasting.ResumeLayout(false);
+ this.tabrecovery.ResumeLayout(false);
+ this.autosavegroupbox.ResumeLayout(false);
+ this.autosavegroupbox.PerformLayout();
+ this.autosavedisabledwarning.ResumeLayout(false);
+ this.autosavedisabledwarning.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.autosavecount)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.autosaveinterval)).EndInit();
this.tabtoasts.ResumeLayout(false);
this.tabtoasts.PerformLayout();
this.groupBox10.ResumeLayout(false);
@@ -2877,5 +3037,17 @@ namespace CodeImp.DoomBuilder.Windows
private System.Windows.Forms.ColumnHeader description;
private System.Windows.Forms.CheckBox cbParallelizedVertexPlotting;
private System.Windows.Forms.CheckBox cbParallelizedLinedefPlotting;
+ private System.Windows.Forms.TabPage tabrecovery;
+ private System.Windows.Forms.GroupBox autosavegroupbox;
+ private Controls.TransparentTrackBar autosaveinterval;
+ private System.Windows.Forms.Label label20;
+ private System.Windows.Forms.CheckBox autosave;
+ private System.Windows.Forms.Label autosaveintervallabel;
+ private System.Windows.Forms.Label autosavecountlabel;
+ private Controls.TransparentTrackBar autosavecount;
+ private System.Windows.Forms.Label label21;
+ private System.Windows.Forms.Panel autosavedisabledwarning;
+ private System.Windows.Forms.Label label34;
+ private System.Windows.Forms.PictureBox pictureBox1;
}
}
\ No newline at end of file
diff --git a/Source/Core/Windows/PreferencesForm.cs b/Source/Core/Windows/PreferencesForm.cs
index 1ea35b3a..6b3c588e 100755
--- a/Source/Core/Windows/PreferencesForm.cs
+++ b/Source/Core/Windows/PreferencesForm.cs
@@ -271,6 +271,11 @@ namespace CodeImp.DoomBuilder.Windows
// Paste options
pasteoptions.Setup(General.Settings.PasteOptions.Copy());
+ // Recovery
+ autosave.Checked = General.Settings.Autosave;
+ autosavecount.Value = General.Settings.AutosaveCount;
+ autosaveinterval.Value = General.Settings.AutosaveInterval;
+
// Toasts
cbToastsEnabled.Checked = General.ToastManager.Enabled;
tbToastDuration.Text = (General.ToastManager.Duration / 1000).ToString();
@@ -452,6 +457,11 @@ namespace CodeImp.DoomBuilder.Windows
// Paste options
General.Settings.PasteOptions = pasteoptions.GetOptions();
+ // Recovery
+ General.Settings.Autosave = autosave.Checked;
+ General.Settings.AutosaveCount = autosavecount.Value;
+ General.Settings.AutosaveInterval = autosaveinterval.Value;
+
// Toasts
General.ToastManager.Enabled = cbToastsEnabled.Checked;
General.ToastManager.Anchor = (ToastAnchor)int.Parse((string)gbToastPosition.Controls.OfType().FirstOrDefault(rb => rb.Checked).Tag);
@@ -462,10 +472,9 @@ namespace CodeImp.DoomBuilder.Windows
if(lvi.Tag is ToastRegistryEntry tre)
General.ToastManager.Registry[tre.Name].Enabled = lvi.Checked;
}
-
+
// Let the plugins know we're closing
General.Plugins.OnClosePreferences(controller);
-
// Close
this.DialogResult = DialogResult.OK;
@@ -1301,6 +1310,25 @@ namespace CodeImp.DoomBuilder.Windows
#endregion
+ #region ================== Recovery
+
+ private void autosave_CheckedChanged(object sender, EventArgs e)
+ {
+ // Enable or disable all controls except the enable/disable checkbox in the group box
+ foreach(Control c in autosavegroupbox.Controls)
+ {
+ if (c == autosave || c == autosavedisabledwarning)
+ continue;
+
+ c.Enabled = autosave.Checked;
+ }
+
+ autosavedisabledwarning.Visible = !autosave.Checked;
+
+ }
+
+ #endregion
+
#region ================== Toasts
private void tbToastDuration_WhenTextChanged(object sender, EventArgs e)
@@ -1332,6 +1360,16 @@ namespace CodeImp.DoomBuilder.Windows
}
}
+ private void autosaveinterval_ValueChanged(object sender, EventArgs e)
+ {
+ autosaveintervallabel.Text = $"{autosaveinterval.Value} minute" + (autosaveinterval.Value > 1 ? "s" : "");
+ }
+
+ private void autosavecount_ValueChanged(object sender, EventArgs e)
+ {
+ autosavecountlabel.Text = autosavecount.Value.ToString();
+ }
+
#endregion
#region ================== Screenshots Stuff (mxd)
diff --git a/Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs
index 4bd62bd5..0f3e8de2 100755
--- a/Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/LinedefsMode.cs
@@ -74,6 +74,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Linedefs that will be edited
ICollection editlines;
+ // Autosave
+ private bool allowautosave;
+
#endregion
#region ================== Properties
@@ -629,6 +632,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
General.Map.Map.ConvertSelection(SelectionType.Linedefs);
UpdateSelectionInfo(); //mxd
SetupSectorLabels(); //mxd
+
+ // By default we allow autosave
+ allowautosave = true;
}
// Mode disengages
@@ -834,11 +840,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
if(General.Interface.IsActiveWindow)
{
+ // Prevent autosave while the editing dialog is shown
+ allowautosave = false;
+
// Show line edit dialog
General.Interface.OnEditFormValuesChanged += linedefEditForm_OnValuesChanged;
DialogResult result = General.Interface.ShowEditLinedefs(editlines);
General.Interface.OnEditFormValuesChanged -= linedefEditForm_OnValuesChanged;
+ allowautosave = true;
+
General.Map.Map.Update();
// Update entire display
@@ -1249,6 +1260,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
CreateBlockmap();
}
+ public override bool OnAutoSaveBegin()
+ {
+ return allowautosave;
+ }
+
//mxd
private void RenderComment(Linedef l)
{
diff --git a/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs
index 7deb7b6d..ae2b569e 100755
--- a/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs
@@ -87,6 +87,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Sectors that will be edited
private ICollection editsectors;
+ // Autosave
+ private bool allowautosave;
+
#endregion
#region ================== Properties
@@ -873,6 +876,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
UpdateSelectedLabels();
UpdateOverlaySurfaces();//mxd
UpdateSelectionInfo(); //mxd
+
+ // By default we allow autosave
+ allowautosave = true;
}
// Mode disengages
@@ -1115,11 +1121,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
if(General.Interface.IsActiveWindow)
{
+ // Prevent autosave while the editing dialog is shown
+ allowautosave = false;
+
//mxd. Show realtime vertex edit dialog
General.Interface.OnEditFormValuesChanged += sectorEditForm_OnValuesChanged;
DialogResult result = General.Interface.ShowEditSectors(editsectors);
General.Interface.OnEditFormValuesChanged -= sectorEditForm_OnValuesChanged;
+ allowautosave = true;
+
General.Map.Renderer2D.UpdateExtraFloorFlag(); //mxd
UpdateEffectLabels();
@@ -1620,6 +1631,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
base.ToggleHighlight();
}
+ public override bool OnAutoSaveBegin()
+ {
+ return allowautosave;
+ }
+
//mxd
private void RenderComment(Sector s)
{
diff --git a/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs
index d83ef8be..247ec261 100755
--- a/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs
@@ -79,6 +79,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Things that will be edited
private ICollection editthings;
+ // Autosave
+ private bool allowautosave;
+
#endregion
#region ================== Properties
@@ -177,6 +180,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
UpdateSelectionInfo(); //mxd
UpdateHelperObjects(); //mxd
SetupSectorLabels(); //mxd
+
+ // By default we allow autosave
+ allowautosave = true;
}
// Mode disengages
@@ -552,11 +558,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Edit only when preferred
if(!thinginserted || BuilderPlug.Me.EditNewThing)
{
+ // Prevent autosave while the editing dialog is shown
+ allowautosave = false;
+
//mxd. Show realtime thing edit dialog
General.Interface.OnEditFormValuesChanged += thingEditForm_OnValuesChanged;
DialogResult result = General.Interface.ShowEditThings(editthings);
General.Interface.OnEditFormValuesChanged -= thingEditForm_OnValuesChanged;
+ allowautosave = true;
+
//mxd. Update helper lines
UpdateHelperObjects();
@@ -828,6 +839,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
}
+ public override bool OnAutoSaveBegin()
+ {
+ return allowautosave;
+ }
+
+
//mxd. Check if any selected thing is outside of map boundary
private static bool CanDrag(ICollection dragthings)
{
diff --git a/Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs b/Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs
index b25ae893..53a9fb94 100755
--- a/Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs
@@ -62,6 +62,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Vertices that will be edited
ICollection editvertices;
+ // Autosave
+ private bool allowautosave;
+
#endregion
#region ================== Properties
@@ -127,6 +130,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Convert geometry selection to vertices only
General.Map.Map.ConvertSelection(SelectionType.Vertices);
UpdateSelectionInfo(); //mxd
+
+ // By default we allow autosave
+ allowautosave = true;
}
// Mode disengages
@@ -409,11 +415,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
if(General.Interface.IsActiveWindow)
{
+ // Prevent autosave while the editing dialog is shown
+ allowautosave = false;
+
//mxd. Show realtime vertex edit dialog
General.Interface.OnEditFormValuesChanged += vertexEditForm_OnValuesChanged;
DialogResult result = General.Interface.ShowEditVertices(editvertices);
General.Interface.OnEditFormValuesChanged -= vertexEditForm_OnValuesChanged;
+ allowautosave = true;
+
// Update entire display
UpdateSelectionInfo(); //mxd
General.Interface.RedrawDisplay();
@@ -659,6 +670,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
}
+ public override bool OnAutoSaveBegin()
+ {
+ return allowautosave;
+ }
+
//mxd. Check if any selected vertex is outside of map boundary
private static bool CanDrag(ICollection dragvertices)
{