timelapsenow/TimelapseApi/TimelapseFileSystem.cs

788 lines
24 KiB
C#

namespace TimelapseApi;
using Eto.Forms;
using Eto.Drawing;
using System.Collections.ObjectModel;
using Dir=System.IO.Directory;
using System.Reflection;
internal class OpenDialog : Dialog<string[]>
{
public enum DialogType
{
Open,
Save,
Directory
}
private static Stream[] GetIcons()
{
Stream[] Strms=new Stream[2];
var asm=Assembly.GetExecutingAssembly();
if(asm != null)
{
var strm=asm.GetManifestResourceStream("TimelapseApi.Assets.directory.png");
if(strm != null)
{
Strms[0]=strm;
}
strm=asm.GetManifestResourceStream("TimelapseApi.Assets.file.png");
if(strm != null)
{
Strms[1]=strm;
}
}
return Strms;
}
private Stream[] Icons =GetIcons();
public class OpenFile
{
public OpenFile(Image icon,string name,string path,bool file)
{
Icon=icon;
Name = name;
Path=path;
IsFile=file;
}
public bool IsFile {get;set;}
public string Path {get;set;}
public string Name {get;set;}
public Image Icon {get;set;}
}
Button upBtn=new Button();
TextBox path=new TextBox();
GridView<OpenFile> view=new GridView<OpenFile>();
ObservableCollection<OpenFile> Files=new ObservableCollection<OpenFile>();
Image[] imgs=new Image[2];
public List<FileFilter> Filters {get;set;}
DialogType Type;
DropDown extPick;
public OpenDialog(TimelapseFileSystem fs,DialogType type)
{
Button cancel=new Button {Text="Cancel"};
cancel.Click +=(sender,e)=>{
Result=new string[0];
this.Close();
};
TextBox filename=new TextBox();
filename.PlaceholderText="Filename";
Button createDir = new Button {Text="Create Folder"};
createDir.Click+=(sender,e)=>{
if(string.IsNullOrWhiteSpace(filename.Text))
{
MessageBox.Show("Please type Filename");
}else{
fs.CreateDirectory(TimelapseFileSystem.CombinePath(Directory,filename.Text));
}
};
extPick=new DropDown();
Type=type;
Filters=new List<FileFilter>();
Title=Type == DialogType.Open ? "Open File" : (Type == DialogType.Save ? "Save File" : "Open Directory");
path.KeyDown+=(sender,e)=>{
if(e.Key==Keys.Enter)
{
ListDirectory();
}
};
upBtn.Text="Up";
upBtn.Click+=(sender,e)=>{
string? path=Path.GetDirectoryName(Directory);
if(!string.IsNullOrWhiteSpace(path))
{
Directory=path;
ListDirectory();
}
};
Resizable=true;
Width=640;
Height=480;
imgs[0]=new Bitmap(Icons[0]);
imgs[1]=new Bitmap(Icons[1]);
Directory="/";
Result=new string[0];
FileSystem=fs;
MultiSelect=false;
view.DataStore=Files;
view.Columns.Add(new GridColumn {DataCell =new ImageViewCell("Icon")});
view.Columns.Add(new GridColumn {HeaderText="Name",DataCell=new TextBoxCell("Name"), Editable=false});
view.SelectedItemsChanged+=(sender,e)=>{
List<string> filenames=new List<string>();
foreach(var v in view.SelectedItems)
{
filenames.Add(v.Name);
}
filename.Text=string.Join(',',filenames);
};
view.CellDoubleClick+=(sender,e)=>{
OpenFile f = (OpenFile)e.Item;
if(f.IsFile)
{
if(Type == DialogType.Save)
{
if(MessageBox.Show($"Do you want to overwrite {f.Path}",MessageBoxButtons.YesNo,MessageBoxType.Question,MessageBoxDefaultButton.No) != DialogResult.Yes)
{
return;
}
}
Result=new string[]{f.Path};
this.Close();
}else{
Directory=f.Path;
ListDirectory();
}
};
Button openBtn = new Button {Text=Type == DialogType.Save ? "Save" : "Open"};
openBtn.Click +=(sender,e)=>{
if(Type == DialogType.Directory)
{
if(view.SelectedItems.Any())
{
Result = new string[]{ view.SelectedItem.Path};
this.Close();
return;
}
if(!string.IsNullOrWhiteSpace(filename.Text))
{
if(!FileSystem.DirectoryExists(Directory))
{
return;
}
string dir =TimelapseFileSystem.CombinePath(Directory,filename.Text);
if(FileSystem.DirectoryExists(dir))
{
Result=new string[] {dir};
this.Close();
return;
}
}
if(FileSystem.DirectoryExists(Directory))
{
Result=new string[] {Directory};
this.Close();
return;
}
}
else
{
if(view.SelectedItems.Any())
{
List<string> paths=new List<string>();
foreach(var item in view.SelectedItems)
{
if(!item.IsFile)
{
return;
}
if(Type==DialogType.Save && FileSystem.FileExists(item.Path))
{
if(MessageBox.Show($"Do you want to overwrite {item.Path}",MessageBoxButtons.YesNo,MessageBoxType.Question,MessageBoxDefaultButton.No) != DialogResult.Yes)
{
return;
}
}
paths.Add(item.Path);
}
Result=paths.ToArray();
this.Close();
return;
}
if(!string.IsNullOrWhiteSpace(filename.Text))
{
string dir =TimelapseFileSystem.CombinePath(Directory,filename.Text);
if(!FileSystem.DirectoryExists(Directory))
{
return;
}
if(Type==DialogType.Open)
{
if(FileSystem.FileExists(dir))
{
Result=new string[]{dir};
this.Close();
return;
}
}else{
if(FileSystem.FileExists(dir))
{
if(MessageBox.Show($"Do you want to overwrite {dir}",MessageBoxButtons.YesNo,MessageBoxType.Question,MessageBoxDefaultButton.No) != DialogResult.Yes)
{
return;
}
Result=new string[]{dir};
this.Close();
return;
}else{
Result=new string[]{dir};
this.Close();
return;
}
}
}
}
};
DynamicLayout lyt=new DynamicLayout();
lyt.BeginVertical();
lyt.BeginHorizontal();
lyt.Add(upBtn);
lyt.Add(path,true);
lyt.EndHorizontal();
lyt.EndBeginVertical();
lyt.BeginHorizontal();
lyt.Add(createDir);
lyt.Add(filename,true);
if(Type!=DialogType.Directory)
{
lyt.Add(extPick);
}
lyt.EndHorizontal();
lyt.EndBeginVertical();
lyt.BeginHorizontal();
lyt.AddSpace(true);
lyt.Add(cancel);
lyt.Add(openBtn);
lyt.EndBeginVertical();
lyt.BeginHorizontal();
lyt.Add(view,true,true);
lyt.EndHorizontal();
lyt.EndVertical();
this.Content=lyt;
}
private void ListDirectory()
{
Files.Clear();
foreach(var dir in FileSystem.GetDirectories(Directory))
{
Files.Add(new OpenFile(imgs[0],dir, TimelapseFileSystem.CombinePath(Directory,dir),false));
}
if(Type != DialogType.Directory){
List<string> validExts=new List<string>();
if(Filters.Count > 0 && extPick.SelectedIndex > -1)
{
foreach(var ext in Filters[extPick.SelectedIndex].Extensions)
{
validExts.Add(ext);
}
}
foreach(var dir in FileSystem.GetFiles(Directory))
{
string ext=Path.GetExtension(dir);
bool add=true;
if(validExts.Count > 0)
{
add=validExts.Contains(ext);
}
if(add)
Files.Add(new OpenFile(imgs[1],dir,TimelapseFileSystem.CombinePath(Directory,dir),true));
}
}
}
protected override void OnShown(EventArgs e)
{
foreach(var item in Filters)
{
extPick.Items.Add(item.Name);
}
if(extPick.Items.Count > 0)
{
extPick.SelectedIndex=0;
}
view.AllowMultipleSelection=MultiSelect;
ListDirectory();
extPick.SelectedIndexChanged+=(sender,e)=>{
ListDirectory();
};
}
protected override void OnClosed(EventArgs e)
{
foreach(var i in imgs)
{
if(i!=null && !i.IsDisposed) i.Dispose();
}
foreach(var i in Icons)
{
if(i!=null) i.Dispose();
}
}
public bool MultiSelect {get {return view.AllowMultipleSelection;}set{view.AllowMultipleSelection=value;}}
public string Directory {get {return path.Text;} set{path.Text=value;}}
public TimelapseFileSystem FileSystem {get;set;}
}
public class Protect : TimelapseFileSystem
{
TimelapseFileSystem fs;
public Protect(TimelapseFileSystem fs)
{
this.fs=fs;
}
public override void CreateDirectory(string path)
{
fs.CreateDirectory(path);
}
public override void DeleteDirectory(string path)
{
fs.DeleteDirectory(path);
}
public override void DeleteFile(string path)
{
fs.DeleteFile(path);
}
public override bool DirectoryExists(string path)
{
return fs.DirectoryExists(path);
}
public override bool FileExists(string path)
{
return fs.FileExists(path);
}
public override Stream Open(string path, FileMode mode, FileAccess access, FileShare share)
{
return fs.Open(path,mode,access,share);
}
protected override IEnumerable<string> GetDirectoriesImpl(string path, string filter = "*")
{
foreach(var items in fs.GetDirectories(path,filter))
{
yield return CombinePath(path,items);
}
}
public override string? ShowSaveDialog(Window parent, FileFilter[] filters, string startDir = "")
{
return fs.ShowSaveDialog(parent, filters, startDir);
}
public override string[] ShowOpenDialog(Window parent, FileFilter[] filters, bool multi, string startDir = "")
{
return fs.ShowOpenDialog(parent, filters, multi, startDir);
}
public override string? ShowDirectoryDialog(Window parent, string startDir = "")
{
return fs.ShowDirectoryDialog(parent,startDir);
}
protected override IEnumerable<string> GetFilesImpl(string path, string filter = "*")
{
foreach(var items in fs.GetFiles(path,filter))
{
yield return CombinePath(path,items);
}
}
}
public class NativeFileSystem : TimelapseFileSystem
{
internal Api? api;
public override void CreateDirectory(string path)
{
Directory.CreateDirectory(path);
}
public override void DeleteDirectory(string path)
{
Directory.Delete(path);
}
public override void DeleteFile(string path)
{
File.Delete(path);
}
public override bool DirectoryExists(string path)
{
return Directory.Exists(path);
}
public override bool FileExists(string path)
{
return File.Exists(path);
}
public override Stream Open(string path, FileMode mode, FileAccess access, FileShare share)
{
return File.Open(path,mode,access,share);
}
protected override IEnumerable<string> GetDirectoriesImpl(string path, string filter = "*")
{
return Directory.EnumerateDirectories(path,filter);
}
public override string? ShowDirectoryDialog(Window parent, string startDir = "")
{
if(api != null && api.Model.useCustomFilePickerForNativeFS) {
return base.ShowDirectoryDialog(parent,startDir);
}
using(var d = new SelectFolderDialog())
{
if(!string.IsNullOrWhiteSpace(startDir))
{
d.Directory = startDir;
}
return d.ShowDialog(parent) == DialogResult.Ok ? d.Directory : null;
}
}
public override string[] ShowOpenDialog(Window parent, FileFilter[] filters,bool multi, string startDir = "")
{
if(api != null && api.Model.useCustomFilePickerForNativeFS) {
return base.ShowOpenDialog(parent,filters,multi,startDir);
}
using(var ofd=new OpenFileDialog())
{
ofd.MultiSelect = multi;
if(!string.IsNullOrWhiteSpace(startDir))
{
ofd.Directory = new Uri(startDir);
}
foreach(var f in filters)
{
ofd.Filters.Add(f);
}
return ofd.ShowDialog(parent) == DialogResult.Ok ? ofd.Filenames.ToArray() : new string[0];
}
}
public override string? ShowSaveDialog(Window parent, FileFilter[] filters, string startDir = "")
{
if(api != null && api.Model.useCustomFilePickerForNativeFS) {
return base.ShowSaveDialog(parent,filters,startDir);
}
using(var sfd=new SaveFileDialog())
{
if(!string.IsNullOrWhiteSpace(startDir))
{
sfd.Directory = new Uri(startDir);
}
foreach(var f in filters)
{
sfd.Filters.Add(f);
}
return sfd.ShowDialog(parent) == DialogResult.Ok ? sfd.FileName : null;
}
}
public override void DeleteDirectory(string path, bool recursive)
{
Directory.Delete(path,recursive);
}
protected override IEnumerable<string> GetFilesImpl(string path, string filter = "*")
{
return Directory.EnumerateFiles(path,filter);
}
}
public abstract class TimelapseFileSystem
{
public string ReadAllText(string path)
{
using (var s=Open(path,FileMode.Open,FileAccess.Read,FileShare.Read))
{
using(var sr=new StreamReader(s))
{
return sr.ReadToEnd();
}
}
}
public void WriteAllText(string path,string? contents)
{
using (var s=Open(path,FileMode.Create,FileAccess.Write,FileShare.None))
{
using(var sw=new StreamWriter(s))
{
sw.Write(contents);
}
}
}
public string? ShowOpenDialog(Window parent,FileFilter[] filters,string startDir="")
{
var res=ShowOpenDialog(parent,filters,false,startDir);
return res.Length == 1 ? res[0] : null;
}
public virtual string[] ShowOpenDialog(Window parent,FileFilter[] filters,bool multi,string startDir="")
{
using(var ofd=new OpenDialog(this,OpenDialog.DialogType.Open))
{
ofd.MultiSelect=multi;
foreach(var f in filters)
{
ofd.Filters.Add(f);
}
if(!string.IsNullOrWhiteSpace(startDir))
{
ofd.Directory=startDir;
}
return ofd.ShowModal(parent);
}
}
public virtual string? ShowSaveDialog(Window parent,FileFilter[] filters,string startDir="")
{
using(var ofd=new OpenDialog(this,OpenDialog.DialogType.Save))
{
foreach(var f in filters)
{
ofd.Filters.Add(f);
}
if(!string.IsNullOrWhiteSpace(startDir))
{
ofd.Directory=startDir;
}
var res=ofd.ShowModal(parent);
return res.Length == 1 ? res[0] : null;
}
}
public virtual string? ShowDirectoryDialog(Window parent,string startDir="")
{
using(var ofd=new OpenDialog(this,OpenDialog.DialogType.Directory))
{
if(!string.IsNullOrWhiteSpace(startDir))
{
ofd.Directory=startDir;
}
var res=ofd.ShowModal(parent);
return res.Length == 1 ? res[0] : null;
}
}
public int GetNumberOfFiles(string path,string filter="*")
{
return GetFilesImpl(path,filter).Count();
}
public int GetNumberOfDirectories(string path,string filter="*")
{
return GetDirectoriesImpl(path,filter).Count();
}
public IEnumerable<string> GetFiles(string path,string filter="*")
{
foreach(var f in GetFilesImpl(path,filter))
{
yield return Path.GetFileName(f);
}
}
public IEnumerable<string> GetDirectories(string path,string filter="*")
{
foreach(var f in GetDirectoriesImpl(path,filter))
{
yield return Path.GetFileName(f);
}
}
public static string CombinePath(params string[] path)
{
if(path.Length==0) return "/";
string path0=path[0];
foreach(var p in path.Skip(1))
{
path0= CombinePath(path0,p);
}
return path0;
}
public abstract void DeleteFile(string path);
public abstract void DeleteDirectory(string path);
public virtual void DeleteDirectory(string path,bool recursive)
{
if(recursive)
{
foreach(var d in GetDirectories(path))
{
DeleteDirectory(CombinePath(path,d),recursive);
}
foreach(var f in GetFiles(path))
{
DeleteFile(CombinePath(path,f));
}
}
DeleteDirectory(path);
}
public abstract void CreateDirectory(string path);
public abstract bool FileExists(string path);
public abstract bool DirectoryExists(string path);
public static string CombinePath(string p1,string p2)
{
return Path.Combine(p1,p2.TrimStart('/')).Replace(Path.DirectorySeparatorChar,'/');
}
public abstract Stream Open(string path,FileMode mode,FileAccess access,FileShare share);
protected abstract IEnumerable<string> GetFilesImpl(string path,string filter="*");
protected abstract IEnumerable<string> GetDirectoriesImpl(string path,string filter="*");
public static SubFileSystem operator+(TimelapseFileSystem fs,string name)
{
SubFileSystem _fs=new SubFileSystem(fs,name);
return _fs;
}
}
public class SubFileSystem : TimelapseFileSystem
{
public SubFileSystem(TimelapseFileSystem fs,string name)
{
var _fs = fs as SubFileSystem;
if(_fs != null)
{
FileSystem = _fs.FileSystem;
CurrentDirectory = CombinePath(_fs.CurrentDirectory,name);
}else{
FileSystem =fs;
CurrentDirectory = CombinePath("/",name);
}
}
public TimelapseFileSystem FileSystem {get;set;}
public string CurrentDirectory {get;set;}
public override void CreateDirectory(string path)
{
FileSystem.CreateDirectory(CombinePath(CurrentDirectory,path));
}
public override void DeleteDirectory(string path)
{
FileSystem.DeleteDirectory(CombinePath(CurrentDirectory,path));
}
public override void DeleteFile(string path)
{
FileSystem.DeleteFile(CombinePath(CurrentDirectory,path));
}
public override bool DirectoryExists(string path)
{
return FileSystem.DirectoryExists(CombinePath(CurrentDirectory,path));
}
public override bool FileExists(string path)
{
return FileSystem.FileExists(CombinePath(CurrentDirectory,path));
}
public override Stream Open(string path, FileMode mode, FileAccess access, FileShare share)
{
return FileSystem.Open(CombinePath(CurrentDirectory,path),mode,access,share);
}
protected override IEnumerable<string> GetDirectoriesImpl(string path, string filter = "*")
{
foreach(var file in FileSystem.GetDirectories(CombinePath(CurrentDirectory,path),filter))
{
yield return Path.GetFileName(file);
}
}
public override string[] ShowOpenDialog(Window parent, FileFilter[] filters, bool multi, string startDir = "")
{
string startDir1;
if(string.IsNullOrWhiteSpace(startDir))
{
startDir1=CurrentDirectory;
}else{
startDir1=CombinePath(CurrentDirectory,startDir);
}
var res=FileSystem.ShowOpenDialog(parent,filters,multi,startDir1);
List<string> validUrls=new List<string>();
foreach(var url in res)
{
if(url.StartsWith(CurrentDirectory))
{
validUrls.Add(url);
}else
{
MessageBox.Show($"You can't open file \"{url}\" because it exists outside of \"{CurrentDirectory}\"",MessageBoxButtons.OK,MessageBoxType.Error);
}
}
return validUrls.ToArray();
}
public override string? ShowSaveDialog(Window parent, FileFilter[] filters, string startDir = "")
{
string startDir1;
if(string.IsNullOrWhiteSpace(startDir))
{
startDir1=CurrentDirectory;
}else{
startDir1=CombinePath(CurrentDirectory,startDir);
}
var url = FileSystem.ShowSaveDialog(parent,filters,startDir1);
if(!string.IsNullOrWhiteSpace(url) && !url.StartsWith(CurrentDirectory))
{
MessageBox.Show($"You can't save file \"{url}\" because it exists outside of \"{CurrentDirectory}\"",MessageBoxButtons.OK,MessageBoxType.Error);
return null;
}
return url;
}
public override string? ShowDirectoryDialog(Window parent, string startDir = "")
{
string startDir1;
if(string.IsNullOrWhiteSpace(startDir))
{
startDir1=CurrentDirectory;
}else{
startDir1=CombinePath(CurrentDirectory,startDir);
}
var url = FileSystem.ShowDirectoryDialog(parent,startDir1);
if(!string.IsNullOrWhiteSpace(url) && !url.StartsWith(CurrentDirectory))
{
MessageBox.Show($"You can't open directory \"{url}\" because it exists outside of \"{CurrentDirectory}\"",MessageBoxButtons.OK,MessageBoxType.Error);
return null;
}
return url;
}
protected override IEnumerable<string> GetFilesImpl(string path, string filter = "*")
{
foreach(var file in FileSystem.GetFiles(CombinePath(CurrentDirectory,path),filter))
{
yield return Path.GetFileName(file);
}
}
public static SubFileSystem operator-(SubFileSystem fs,int num)
{
string path=fs.CurrentDirectory;
for(int i = 0;i < num;i++)
{
string? p=Path.GetDirectoryName(path);
if(string.IsNullOrWhiteSpace(p))
{
path="/";
break;
}else{
path = p;
}
}
return new SubFileSystem(fs.FileSystem,path);
}
}