namespace TimelapseApi; using Eto.Forms; using Eto.Drawing; using System.Collections.ObjectModel; using Dir=System.IO.Directory; using System.Reflection; internal class OpenDialog : Dialog { 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 view=new GridView(); ObservableCollection Files=new ObservableCollection(); Image[] imgs=new Image[2]; public List 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(); 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 filenames=new List(); 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 paths=new List(); 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 validExts=new List(); 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 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 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 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 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 GetFiles(string path,string filter="*") { foreach(var f in GetFilesImpl(path,filter)) { yield return Path.GetFileName(f); } } public IEnumerable 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 GetFilesImpl(string path,string filter="*"); protected abstract IEnumerable 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 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 validUrls=new List(); 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 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); } }