actually implement AutoSave plugin, and add NonSerializedProperty where appropriate to FileSystem objects
This commit is contained in:
parent
97c9e605c4
commit
f439eb6513
@ -0,0 +1,36 @@
|
||||
//
|
||||
// NonSerializedPropertyAttribute.cs - indicates that a particular property should not be serialized
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2011-2020 Mike Becker's Software
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
|
||||
namespace UniversalEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that a particular property should not be serialized. Used in
|
||||
/// place of <see cref="NonSerializedAttribute" /> because that does
|
||||
/// not allow use on property declarations.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class NonSerializedPropertyAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@ -126,23 +126,20 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
}
|
||||
}
|
||||
|
||||
private FileAttributes mvarAttributes = FileAttributes.None;
|
||||
public FileAttributes Attributes { get { return mvarAttributes; } set { mvarAttributes = value; } }
|
||||
|
||||
private string mvarName = String.Empty;
|
||||
public FileAttributes Attributes { get; set; } = FileAttributes.None;
|
||||
/// <summary>
|
||||
/// The name of this file.
|
||||
/// </summary>
|
||||
public string Name { get { return mvarName; } set { mvarName = value; } }
|
||||
public string Name { get; set; } = String.Empty;
|
||||
|
||||
public void SetData(byte[] data)
|
||||
{
|
||||
mvarSource = new MemoryFileSource(data);
|
||||
Source = new MemoryFileSource(data);
|
||||
}
|
||||
|
||||
public byte[] GetData()
|
||||
{
|
||||
if (mvarSource != null) return mvarSource.GetData();
|
||||
if (Source != null) return Source.GetData();
|
||||
if (DataRequest != null)
|
||||
{
|
||||
DataRequestEventArgs e = new DataRequestEventArgs();
|
||||
@ -150,15 +147,15 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
return e.Data;
|
||||
}
|
||||
|
||||
Console.WriteLine("DataRequest: " + mvarName + ": No source associated with this file");
|
||||
Console.WriteLine("DataRequest: " + Name + ": No source associated with this file");
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public byte[] GetData(long offset, long length)
|
||||
{
|
||||
if (mvarSource != null) return mvarSource.GetDataInternal(offset, length);
|
||||
if (Source != null) return Source.GetDataInternal(offset, length);
|
||||
|
||||
Console.WriteLine("DataRequest: " + mvarName + ": No source associated with this file");
|
||||
Console.WriteLine("DataRequest: " + Name + ": No source associated with this file");
|
||||
return new byte[length];
|
||||
}
|
||||
|
||||
@ -170,7 +167,7 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
}
|
||||
public void SetData(System.IO.Stream stream)
|
||||
{
|
||||
mvarSource = new AccessorFileSource(new StreamAccessor(stream));
|
||||
Source = new AccessorFileSource(new StreamAccessor(stream));
|
||||
}
|
||||
|
||||
public void SetData(string value)
|
||||
@ -185,14 +182,14 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
public object Clone()
|
||||
{
|
||||
File clone = new File();
|
||||
clone.Name = mvarName;
|
||||
clone.Source = mvarSource;
|
||||
clone.Name = Name;
|
||||
clone.Source = Source;
|
||||
if (DataRequest != null)
|
||||
{
|
||||
clone.DataRequest += DataRequest;
|
||||
}
|
||||
clone.Source = Source;
|
||||
foreach (KeyValuePair<string, object> kvp in mvarProperties)
|
||||
foreach (KeyValuePair<string, object> kvp in Properties)
|
||||
{
|
||||
clone.Properties.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
@ -200,7 +197,7 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
{
|
||||
clone.AdditionalDetails.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
clone.Parent = mvarParent;
|
||||
clone.Parent = Parent;
|
||||
return clone;
|
||||
}
|
||||
|
||||
@ -266,7 +263,7 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
strSize = "?";
|
||||
}
|
||||
}
|
||||
return mvarName + " [" + strSize + "]";
|
||||
return Name + " [" + strSize + "]";
|
||||
}
|
||||
|
||||
public void Save()
|
||||
@ -281,7 +278,7 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
System.IO.Directory.CreateDirectory(FileDirectory);
|
||||
}
|
||||
|
||||
FileSource source = mvarSource;
|
||||
FileSource source = Source;
|
||||
long blockSize = (System.Environment.WorkingSet / 8);
|
||||
long blockCount = (blockSize / source.GetLength());
|
||||
long offset = 0;
|
||||
@ -310,9 +307,9 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mvarSource != null)
|
||||
if (Source != null)
|
||||
{
|
||||
return mvarSource.GetLength();
|
||||
return Source.GetLength();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -328,31 +325,23 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
mvarSize = null;
|
||||
}
|
||||
|
||||
#region Metadata
|
||||
private string mvarTitle = null;
|
||||
public string Title { get { return mvarTitle; } set { mvarTitle = value; } }
|
||||
|
||||
private string mvarDescription = null;
|
||||
public string Description { get { return mvarDescription; } set { mvarDescription = value; } }
|
||||
#endregion
|
||||
public string Title { get; set; } = null;
|
||||
public string Description { get; set; } = null;
|
||||
|
||||
public event DataRequestEventHandler DataRequest;
|
||||
|
||||
private Dictionary<string, object> mvarProperties = new Dictionary<string, object>();
|
||||
public Dictionary<string, object> Properties { get { return mvarProperties; } }
|
||||
public Dictionary<string, object> Properties { get; } = new Dictionary<string, object>();
|
||||
public DateTime ModificationTimestamp { get; set; } = DateTime.Now;
|
||||
|
||||
private DateTime mvarModificationTimestamp = DateTime.Now;
|
||||
public DateTime ModificationTimestamp { get { return mvarModificationTimestamp; } set { mvarModificationTimestamp = value; } }
|
||||
|
||||
private FileSource mvarSource = null;
|
||||
/// <summary>
|
||||
/// Determines where this <see cref="File" /> gets its data from.
|
||||
/// </summary>
|
||||
public FileSource Source { get { return mvarSource; } set { mvarSource = value; } }
|
||||
public FileSource Source { get; set; } = null;
|
||||
|
||||
private IFileSystemContainer mvarParent = null;
|
||||
public IFileSystemContainer Parent { get { return mvarParent; } internal set { mvarParent = value; } }
|
||||
[NonSerializedProperty]
|
||||
public IFileSystemContainer Parent { get; internal set; } = null;
|
||||
|
||||
[NonSerializedProperty]
|
||||
public FileSystemObjectModel FileSystem { get; private set; } = null;
|
||||
|
||||
// The amount of working set to allocate to each block.
|
||||
@ -370,7 +359,7 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
long blockSize = (System.Environment.SystemPageSize / BLOCK_FRACTION);
|
||||
|
||||
long offset = 0;
|
||||
double dbl = ((double)mvarSource.GetLength() / (double)blockSize);
|
||||
double dbl = ((double)Source.GetLength() / (double)blockSize);
|
||||
long blockCount = (long)Math.Ceiling(dbl);
|
||||
|
||||
if (transformations != null)
|
||||
@ -380,7 +369,7 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
|
||||
for (long i = 0; i < blockCount; i++)
|
||||
{
|
||||
byte[] data = mvarSource.GetDataInternal(offset, blockSize);
|
||||
byte[] data = Source.GetDataInternal(offset, blockSize);
|
||||
offset += blockSize;
|
||||
|
||||
bw.WriteBytes(data);
|
||||
|
||||
@ -66,7 +66,10 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
}
|
||||
}
|
||||
|
||||
[NonSerializedProperty]
|
||||
public FileSystemObjectModel FileSystem { get { return this; } }
|
||||
[NonSerializedProperty]
|
||||
public IFileSystemContainer Parent { get { return null; } }
|
||||
|
||||
public static FileSystemObjectModel FromFiles(string[] fileNames)
|
||||
{
|
||||
@ -192,9 +195,9 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
}
|
||||
}
|
||||
|
||||
public object FindObject(string name)
|
||||
public IFileSystemObject FindObject(string name)
|
||||
{
|
||||
string[] path = name.Split(new char[] { '/' });
|
||||
string[] path = name.Split(new char[] { '/', '\\' });
|
||||
Folder parent = null;
|
||||
for (int i = 0; i < path.Length - 1; i++)
|
||||
{
|
||||
|
||||
@ -106,6 +106,7 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
mvarFiles = new File.FileCollection(this);
|
||||
}
|
||||
|
||||
[NonSerializedProperty]
|
||||
public FileSystemObjectModel FileSystem { get; private set; } = null;
|
||||
|
||||
private FolderCollection _parentCollection = null;
|
||||
@ -113,8 +114,8 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
private string mvarName = String.Empty;
|
||||
public string Name { get { return mvarName; } set { mvarName = value; } }
|
||||
|
||||
private IFileSystemContainer mvarParent = null;
|
||||
public IFileSystemContainer Parent { get { return mvarParent; } private set { mvarParent = value; } }
|
||||
[NonSerializedProperty]
|
||||
public IFileSystemContainer Parent { get; private set; } = null;
|
||||
|
||||
private FolderCollection mvarFolders = null;
|
||||
public FolderCollection Folders { get { return mvarFolders; } }
|
||||
|
||||
@ -24,15 +24,12 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
/// <summary>
|
||||
/// The interface which defines the base functionality for an object which contains <see cref="File" />s and <see cref="Folder" />s.
|
||||
/// </summary>
|
||||
public interface IFileSystemContainer
|
||||
public interface IFileSystemContainer : IFileSystemObject
|
||||
{
|
||||
File.FileCollection Files { get; }
|
||||
Folder.FolderCollection Folders { get; }
|
||||
string GetNewFolderName();
|
||||
|
||||
File AddFile(string name, byte[] fileData = null);
|
||||
|
||||
FileSystemObjectModel FileSystem { get; }
|
||||
string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@ namespace UniversalEditor.ObjectModels.FileSystem
|
||||
string Name { get; set; }
|
||||
|
||||
FileSystemObjectModel FileSystem { get; }
|
||||
IFileSystemContainer Parent { get; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents a <see cref="System.Collections.ObjectModel.Collection{IFileSystemObject}" /> of <see cref="IFileSystemObject" />s.
|
||||
|
||||
251
Plugins/UniversalEditor.Plugins.AutoSave/AutoSaveDataFormat.cs
Normal file
251
Plugins/UniversalEditor.Plugins.AutoSave/AutoSaveDataFormat.cs
Normal file
@ -0,0 +1,251 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UniversalEditor.Accessors;
|
||||
using UniversalEditor.IO;
|
||||
|
||||
namespace UniversalEditor.Plugins.AutoSave
|
||||
{
|
||||
internal class AutoSaveDataFormat : DataFormat
|
||||
{
|
||||
public float Version { get; set; } = 1.0f;
|
||||
|
||||
public static float MAX_SUPPORTED_VERSION = 1.0f;
|
||||
|
||||
protected override void LoadInternal(ref ObjectModel objectModel)
|
||||
{
|
||||
Reader r = Accessor.Reader;
|
||||
AutoSaveObjectModel autosave = (objectModel as AutoSaveObjectModel);
|
||||
|
||||
string signature = r.ReadFixedLengthString(12);
|
||||
if (!signature.Equals("UE4 AutoSave"))
|
||||
throw new InvalidDataFormatException("file does not begin with 'UE4 AutoSave'");
|
||||
|
||||
Version = r.ReadSingle();
|
||||
if (Version > MAX_SUPPORTED_VERSION)
|
||||
{
|
||||
throw new InvalidDataFormatException(String.Format("format version {0} not supported!", Version));
|
||||
}
|
||||
|
||||
autosave.OriginalFileName = r.ReadNullTerminatedString();
|
||||
autosave.LastUpdateDateTime = r.ReadDateTime();
|
||||
|
||||
long offsetToStringTable = r.ReadInt64();
|
||||
r.Accessor.SavePosition();
|
||||
|
||||
r.Seek(offsetToStringTable, SeekOrigin.Begin);
|
||||
int stringTableCount = r.ReadInt32();
|
||||
for (int i = 0; i < stringTableCount; i++)
|
||||
{
|
||||
string value = r.ReadNullTerminatedString();
|
||||
_StringTable.Add(value);
|
||||
}
|
||||
|
||||
r.Accessor.LoadPosition();
|
||||
|
||||
object om = ReadObject(r);
|
||||
if (om is ObjectModel)
|
||||
{
|
||||
autosave.ObjectModel = (om as ObjectModel);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveInternal(ObjectModel objectModel)
|
||||
{
|
||||
Writer w = Accessor.Writer;
|
||||
AutoSaveObjectModel autosave = (objectModel as AutoSaveObjectModel);
|
||||
|
||||
w.AutoFlush = true; // should be removed for release
|
||||
|
||||
w.WriteFixedLengthString("UE4 AutoSave");
|
||||
w.WriteSingle(Version);
|
||||
|
||||
if (autosave.OriginalFileName != null)
|
||||
{
|
||||
w.WriteNullTerminatedString(autosave.OriginalFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
w.WriteByte(0);
|
||||
}
|
||||
|
||||
// last update date time
|
||||
w.WriteDateTime(DateTime.Now);
|
||||
|
||||
MemoryAccessor ma = new MemoryAccessor();
|
||||
WriteObject(ma.Writer, autosave.ObjectModel);
|
||||
|
||||
ma.Flush();
|
||||
ma.Close();
|
||||
|
||||
w.WriteInt64(ma.Length); // offset to string table
|
||||
w.WriteBytes(ma.ToArray());
|
||||
|
||||
w.WriteInt32(_StringTable.Count);
|
||||
for (int i = 0; i < _StringTable.Count; i++)
|
||||
{
|
||||
w.WriteNullTerminatedString(_StringTable[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private System.Collections.Specialized.StringCollection _StringTable = new System.Collections.Specialized.StringCollection();
|
||||
private int MakeStringTableEntry(string value)
|
||||
{
|
||||
if (!_StringTable.Contains(value))
|
||||
_StringTable.Add(value);
|
||||
|
||||
return _StringTable.IndexOf(value);
|
||||
}
|
||||
|
||||
private object ReadObject(Reader r)
|
||||
{
|
||||
AutoSaveKnownType typeId = (AutoSaveKnownType) r.ReadInt32();
|
||||
switch (typeId)
|
||||
{
|
||||
case AutoSaveKnownType.Null: return null;
|
||||
case AutoSaveKnownType.Object:
|
||||
{
|
||||
int index = r.ReadInt32();
|
||||
string typeName = _StringTable[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void WriteObject(Writer w, object o)
|
||||
{
|
||||
if (o == null)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.Null); // null
|
||||
return;
|
||||
}
|
||||
|
||||
w.WriteInt32((int)AutoSaveKnownType.Object);
|
||||
|
||||
Type t = o.GetType();
|
||||
|
||||
w.WriteInt32(MakeStringTableEntry(t.FullName));
|
||||
|
||||
// public instance properties are really the only things we care about
|
||||
System.Reflection.PropertyInfo[] pis = t.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
System.Collections.Generic.List<PropertyInfo> list = new System.Collections.Generic.List<PropertyInfo>();
|
||||
for (int i = 0; i < pis.Length; i++)
|
||||
{
|
||||
if (pis[i].GetCustomAttribute<NonSerializedPropertyAttribute>() != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
list.Add(pis[i]);
|
||||
}
|
||||
w.WriteInt32(list.Count);
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
WriteProperty(w, list[i], o);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteProperty(Writer w, PropertyInfo propertyInfo, object obj)
|
||||
{
|
||||
w.WriteInt32(MakeStringTableEntry(propertyInfo.Name));
|
||||
w.WriteInt32(MakeStringTableEntry(propertyInfo.PropertyType.FullName));
|
||||
|
||||
object val = propertyInfo.GetValue(obj);
|
||||
|
||||
if (val == obj)
|
||||
return;
|
||||
|
||||
WriteValue(w, val);
|
||||
}
|
||||
|
||||
private void WriteValue(Writer w, object val)
|
||||
{
|
||||
if (val is string)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.String);
|
||||
w.WriteInt32(MakeStringTableEntry(val as string));
|
||||
}
|
||||
else if (val is byte)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.Byte);
|
||||
w.WriteByte((byte)val);
|
||||
}
|
||||
else if (val is sbyte)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.SByte);
|
||||
w.WriteSByte((sbyte)val);
|
||||
}
|
||||
else if (val is char)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.Char);
|
||||
w.WriteChar((char)val);
|
||||
}
|
||||
else if (val is short)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.Int16);
|
||||
w.WriteInt16((short)val);
|
||||
}
|
||||
else if (val is int)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.Int32);
|
||||
w.WriteInt32((int)val);
|
||||
}
|
||||
else if (val is long)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.Int64);
|
||||
w.WriteInt64((long)val);
|
||||
}
|
||||
else if (val is ushort)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.UInt16);
|
||||
w.WriteUInt16((ushort)val);
|
||||
}
|
||||
else if (val is uint)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.UInt32);
|
||||
w.WriteUInt32((uint)val);
|
||||
}
|
||||
else if (val is ulong)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.UInt64);
|
||||
w.WriteUInt64((ulong)val);
|
||||
}
|
||||
else if (val is float)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.Single);
|
||||
w.WriteSingle((float)val);
|
||||
}
|
||||
else if (val is double)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.Double);
|
||||
w.WriteDouble((double)val);
|
||||
}
|
||||
else if (val is decimal)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.Decimal);
|
||||
w.WriteDecimal((decimal)val);
|
||||
}
|
||||
else if (val is Guid)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.Guid);
|
||||
w.WriteGuid((Guid)val);
|
||||
}
|
||||
else if (val is System.Collections.IList)
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.List);
|
||||
|
||||
System.Collections.IList il = (val as System.Collections.IList);
|
||||
w.WriteInt32(il.Count);
|
||||
for (int i = 0; i < il.Count; i++)
|
||||
{
|
||||
WriteValue(w, il[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
w.WriteInt32((int)AutoSaveKnownType.Object);
|
||||
WriteObject(w, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
namespace UniversalEditor.Plugins.AutoSave
|
||||
{
|
||||
public enum AutoSaveKnownType
|
||||
{
|
||||
Object = -1,
|
||||
Null = 0,
|
||||
String,
|
||||
Char,
|
||||
Byte,
|
||||
SByte,
|
||||
Int16,
|
||||
Int32,
|
||||
Int64,
|
||||
UInt16,
|
||||
UInt32,
|
||||
UInt64,
|
||||
Single,
|
||||
Double,
|
||||
Decimal,
|
||||
Guid,
|
||||
List
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
//
|
||||
// AutoSaveObjectModel.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2021 Mike Becker's Software
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace UniversalEditor.Plugins.AutoSave
|
||||
{
|
||||
public class AutoSaveObjectModel : ObjectModel
|
||||
{
|
||||
public ObjectModel ObjectModel { get; set; } = null;
|
||||
|
||||
public string OriginalFileName { get; set; } = null;
|
||||
public DateTime LastUpdateDateTime { get; set; } = DateTime.Now;
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
}
|
||||
|
||||
public override void CopyTo(ObjectModel where)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MBS.Framework;
|
||||
using MBS.Framework.UserInterface;
|
||||
using UniversalEditor.Accessors;
|
||||
using UniversalEditor.Plugins.AutoSave.Dialogs;
|
||||
using UniversalEditor.UserInterface;
|
||||
|
||||
namespace UniversalEditor.Plugins.AutoSave
|
||||
{
|
||||
@ -27,19 +32,86 @@ namespace UniversalEditor.Plugins.AutoSave
|
||||
{
|
||||
private Timer tmr = new Timer();
|
||||
|
||||
private AutoSaveDataFormat asdf = new AutoSaveDataFormat();
|
||||
|
||||
private string GetAutosavePath()
|
||||
{
|
||||
return String.Format("/tmp/autosave/{0}", Application.Instance.ShortName);
|
||||
}
|
||||
|
||||
private void tmr_Tick(object sender, EventArgs e)
|
||||
{
|
||||
Console.WriteLine("autosave: looking for dirty documents...");
|
||||
|
||||
IHostApplication ha = (Application.Instance as IHostApplication);
|
||||
|
||||
string path = System.IO.Path.Combine(new string[] { GetAutosavePath(), DateTime.Now.ToString("yyyyMMdd") });
|
||||
Console.WriteLine("autosave: saving dirty documents in /tmp/autosave/universal-editor/...");
|
||||
|
||||
for (int i = 0; i < ha.CurrentWindow.Editors.Count; i++)
|
||||
{
|
||||
Editor ed = ha.CurrentWindow.Editors[i];
|
||||
if (ed.Changed || !ed.Document.IsSaved)
|
||||
{
|
||||
string filename = System.IO.Path.Combine(new string[] { path, String.Format("{0}{1}.tmp", DateTime.Now.ToString("HHmmss"), i.ToString().PadLeft(2, '0')) });
|
||||
|
||||
string dir = System.IO.Path.GetDirectoryName(filename);
|
||||
if (!System.IO.Directory.Exists(dir))
|
||||
System.IO.Directory.CreateDirectory(dir);
|
||||
|
||||
if (!fas.ContainsKey(ed))
|
||||
{
|
||||
fas[ed] = new FileAccessor(filename, true, true);
|
||||
}
|
||||
|
||||
AutoSaveObjectModel autosave = new AutoSaveObjectModel();
|
||||
autosave.ObjectModel = ed.ObjectModel;
|
||||
if (ed.Document.IsSaved)
|
||||
{
|
||||
autosave.OriginalFileName = ed.Document.Accessor.GetFileName();
|
||||
}
|
||||
else
|
||||
{
|
||||
autosave.OriginalFileName = null;
|
||||
}
|
||||
Document.Save(autosave, asdf, fas[ed]);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("autosave: going back to sleep");
|
||||
}
|
||||
|
||||
private System.Collections.Generic.Dictionary<Editor, FileAccessor> fas = new System.Collections.Generic.Dictionary<Editor, FileAccessor>();
|
||||
|
||||
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
base.InitializeInternal();
|
||||
|
||||
tmr.Duration = 5 /*minutes*/ * 60 /*seconds in a minute*/ * 1000 /*milliseconds in a second*/;
|
||||
// check to see if we have any dirty documents
|
||||
Console.WriteLine("autosave: checking for existing dirty documents...");
|
||||
|
||||
string path = GetAutosavePath();
|
||||
if (!System.IO.Directory.Exists(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] autosaves = System.IO.Directory.GetFiles(path, "*.tmp", System.IO.SearchOption.AllDirectories);
|
||||
|
||||
if (autosaves.Length > 0)
|
||||
{
|
||||
AutoSaveDialog dlg = new AutoSaveDialog();
|
||||
dlg.FileNames.AddRange(autosaves);
|
||||
if (dlg.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tmr.Duration = 10000; // 5 /*minutes*/ * 60 /*seconds in a minute*/ * 1000 /*milliseconds in a second*/;
|
||||
tmr.Tick += tmr_Tick;
|
||||
tmr.Enabled = true;
|
||||
}
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using MBS.Framework;
|
||||
using MBS.Framework.Drawing;
|
||||
using MBS.Framework.UserInterface;
|
||||
using MBS.Framework.UserInterface.Controls;
|
||||
using MBS.Framework.UserInterface.Controls.ListView;
|
||||
using UniversalEditor.Accessors;
|
||||
using UniversalEditor.IO;
|
||||
|
||||
namespace UniversalEditor.Plugins.AutoSave.Dialogs
|
||||
{
|
||||
[ContainerLayout(typeof(AutoSaveDialog), "UniversalEditor.Plugins.AutoSave.Dialogs.AutoSaveDialog.glade")]
|
||||
public class AutoSaveDialog : CustomDialog
|
||||
{
|
||||
private Label lblPrompt;
|
||||
private ListViewControl lv;
|
||||
|
||||
public System.Collections.Specialized.StringCollection FileNames { get; } = new System.Collections.Specialized.StringCollection();
|
||||
|
||||
protected override void OnCreated(EventArgs e)
|
||||
{
|
||||
base.OnCreated(e);
|
||||
|
||||
lblPrompt.Text = lblPrompt.Text.Replace("${Application.Title}", Application.Instance.Title);
|
||||
|
||||
Document[] ds = new Document[FileNames.Count];
|
||||
for (int i = 0; i < ds.Length; i++)
|
||||
{
|
||||
ds[i] = new Document(new AutoSaveObjectModel(), new AutoSaveDataFormat(), new FileAccessor(FileNames[i], false, false, true));
|
||||
ds[i].Load();
|
||||
|
||||
string fn = (ds[i].ObjectModel as AutoSaveObjectModel).OriginalFileName;
|
||||
fn = String.IsNullOrEmpty(fn) ? "(untitled)" : fn;
|
||||
|
||||
string dts = String.Format("{0} {1}", (ds[i].ObjectModel as AutoSaveObjectModel).LastUpdateDateTime.ToLongDateString(), (ds[i].ObjectModel as AutoSaveObjectModel).LastUpdateDateTime.ToLongTimeString());
|
||||
lv.Model.Rows.Add(new TreeModelRow(new TreeModelRowColumn[]
|
||||
{
|
||||
new TreeModelRowColumn(lv.Model.Columns[0], fn),
|
||||
new TreeModelRowColumn(lv.Model.Columns[1], dts)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.2 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<object class="GtkTreeStore" id="tmFiles">
|
||||
<columns>
|
||||
<!-- column-name colFileFileName -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name colFileLastModificationDateTime -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkDialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="default_width">600</property>
|
||||
<property name="default_height">400</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="cmdOK">
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<style>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="cmdCancel">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">16</property>
|
||||
<property name="margin_right">16</property>
|
||||
<property name="margin_top">16</property>
|
||||
<property name="margin_bottom">16</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_right">16</property>
|
||||
<property name="stock">gtk-dialog-info</property>
|
||||
<property name="icon_size">6</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="lblPrompt">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">${Application.Title} has recovered the following files. Save the ones you wish to keep.</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="lv">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">tmFiles</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="chFileFileName">
|
||||
<property name="resizable">True</property>
|
||||
<property name="title" translatable="yes">File name</property>
|
||||
<property name="clickable">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="chFileLastModificationDateTime">
|
||||
<property name="resizable">True</property>
|
||||
<property name="title" translatable="yes">Last modified</property>
|
||||
<property name="clickable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@ -33,6 +33,10 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="AutoSavePlugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="AutoSaveDataFormat.cs" />
|
||||
<Compile Include="AutoSaveKnownType.cs" />
|
||||
<Compile Include="Dialogs\AutoSaveDialog.cs" />
|
||||
<Compile Include="AutoSaveObjectModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\MBS.Framework\MBS.Framework\MBS.Framework.csproj">
|
||||
@ -43,6 +47,20 @@
|
||||
<Project>{29E1C1BB-3EA5-4062-B62F-85EEC703FE07}</Project>
|
||||
<Name>MBS.Framework.UserInterface</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Libraries\UniversalEditor.UserInterface\UniversalEditor.UserInterface.csproj">
|
||||
<Project>{8622EBC4-8E20-476E-B284-33D472081F5C}</Project>
|
||||
<Name>UniversalEditor.UserInterface</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Libraries\UniversalEditor.Core\UniversalEditor.Core.csproj">
|
||||
<Project>{2D4737E6-6D95-408A-90DB-8DFF38147E85}</Project>
|
||||
<Name>UniversalEditor.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Dialogs\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Dialogs\AutoSaveDialog.glade" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
Loading…
x
Reference in New Issue
Block a user