using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Dalamud.Utility;
namespace Dalamud.Interface.ImGuiFileDialog;
///
/// A file or folder picker.
///
public partial class FileDialog
{
private readonly Lock filesLock = new();
private readonly DriveListLoader driveListLoader = new();
private readonly List files = [];
private readonly List filteredFiles = [];
private SortingField currentSortingField = SortingField.FileName;
/// Fired whenever the sorting field changes.
public event Action? SortOrderChanged;
/// The sorting type of the file selector.
public enum SortingField
{
/// No sorting specified.
None = 0,
/// Sort for ascending file names in culture-specific order.
FileName = 1,
/// Sort for ascending file types in culture-specific order.
Type = 2,
/// Sort for ascending file sizes.
Size = 3,
/// Sort for ascending last update dates.
Date = 4,
/// Sort for descending file names in culture-specific order.
FileNameDescending = 5,
/// Sort for descending file types in culture-specific order.
TypeDescending = 6,
/// Sort for descending file sizes.
SizeDescending = 7,
/// Sort for descending last update dates.
DateDescending = 8,
}
private enum FileStructType
{
File,
Directory,
}
/// Specify the current and subsequent sort order.
/// The new sort order. None is invalid and will not have any effect.
public void SortFields(SortingField sortingField)
{
Comparison? sortFunc = sortingField switch
{
SortingField.FileName => SortByFileNameAsc,
SortingField.FileNameDescending => SortByFileNameDesc,
SortingField.Type => SortByTypeAsc,
SortingField.TypeDescending => SortByTypeDesc,
SortingField.Size => SortBySizeAsc,
SortingField.SizeDescending => SortBySizeDesc,
SortingField.Date => SortByDateAsc,
SortingField.DateDescending => SortByDateDesc,
_ => null,
};
if (sortFunc is null)
{
return;
}
this.files.Sort(sortFunc);
this.currentSortingField = sortingField;
this.ApplyFilteringOnFileList();
this.SortOrderChanged?.InvokeSafely(this.currentSortingField);
}
private static string ComposeNewPath(List decomposition)
{
switch (decomposition.Count)
{
// Handle UNC paths (network paths)
case >= 2 when string.IsNullOrEmpty(decomposition[0]) && string.IsNullOrEmpty(decomposition[1]):
var pathParts = new List(decomposition);
pathParts.RemoveRange(0, 2);
// Can not access server level or UNC root
if (pathParts.Count <= 1)
{
return string.Empty;
}
return $@"\\{string.Join('\\', pathParts)}";
case 1:
var drivePath = decomposition[0];
if (drivePath[^1] != Path.DirectorySeparatorChar)
{ // turn C: into C:\
drivePath += Path.DirectorySeparatorChar;
}
return drivePath;
default: return Path.Combine(decomposition.ToArray());
}
}
private static FileStruct GetFile(FileInfo file, string path)
=> new()
{
FileName = file.Name,
FilePath = path,
FileModifiedDate = FormatModifiedDate(file.LastWriteTime),
FileSize = file.Length,
FormattedFileSize = BytesToString(file.Length),
Type = FileStructType.File,
Ext = file.Extension.Trim('.'),
};
private static FileStruct GetDir(DirectoryInfo dir, string path)
=> new()
{
FileName = dir.Name,
FilePath = path,
FileModifiedDate = FormatModifiedDate(dir.LastWriteTime),
FileSize = 0,
FormattedFileSize = string.Empty,
Type = FileStructType.Directory,
Ext = string.Empty,
};
private static int SortByFileNameDesc(FileStruct a, FileStruct b)
{
switch (a.FileName, b.FileName)
{
case ("..", ".."): return 0;
case ("..", _): return -1;
case (_, ".."): return 1;
}
if (a.FileName[0] is '.')
{
if (b.FileName[0] is not '.')
{
return 1;
}
if (a.FileName.Length is 1)
{
return -1;
}
if (b.FileName.Length is 1)
{
return 1;
}
return -1 * string.Compare(a.FileName[1..], b.FileName[1..], StringComparison.CurrentCulture);
}
if (b.FileName[0] is '.')
{
return -1;
}
if (a.Type != b.Type)
{
return a.Type is FileStructType.Directory ? 1 : -1;
}
return -string.Compare(a.FileName, b.FileName, StringComparison.CurrentCulture);
}
private static int SortByFileNameAsc(FileStruct a, FileStruct b)
{
switch (a.FileName, b.FileName)
{
case ("..", ".."): return 0;
case ("..", _): return -1;
case (_, ".."): return 1;
}
if (a.FileName[0] is '.')
{
if (b.FileName[0] is not '.')
{
return -1;
}
if (a.FileName.Length is 1)
{
return 1;
}
if (b.FileName.Length is 1)
{
return -1;
}
return string.Compare(a.FileName[1..], b.FileName[1..], StringComparison.CurrentCulture);
}
if (b.FileName[0] is '.')
{
return 1;
}
if (a.Type != b.Type)
{
return a.Type is FileStructType.Directory ? -1 : 1;
}
return string.Compare(a.FileName, b.FileName, StringComparison.CurrentCulture);
}
private static int SortByTypeDesc(FileStruct a, FileStruct b)
{
switch (a.FileName, b.FileName)
{
case ("..", ".."): return 0;
case ("..", _): return -1;
case (_, ".."): return 1;
}
if (a.Type != b.Type)
{
return (a.Type == FileStructType.Directory) ? 1 : -1;
}
return string.Compare(a.Ext, b.Ext, StringComparison.CurrentCulture);
}
private static int SortByTypeAsc(FileStruct a, FileStruct b)
{
switch (a.FileName, b.FileName)
{
case ("..", ".."): return 0;
case ("..", _): return -1;
case (_, ".."): return 1;
}
if (a.Type != b.Type)
{
return (a.Type == FileStructType.Directory) ? -1 : 1;
}
return -string.Compare(a.Ext, b.Ext, StringComparison.CurrentCulture);
}
private static int SortBySizeDesc(FileStruct a, FileStruct b)
{
switch (a.FileName, b.FileName)
{
case ("..", ".."): return 0;
case ("..", _): return -1;
case (_, ".."): return 1;
}
if (a.Type != b.Type)
{
return (a.Type is FileStructType.Directory) ? 1 : -1;
}
return a.FileSize.CompareTo(b.FileSize);
}
private static int SortBySizeAsc(FileStruct a, FileStruct b)
{
switch (a.FileName, b.FileName)
{
case ("..", ".."): return 0;
case ("..", _): return -1;
case (_, ".."): return 1;
}
if (a.Type != b.Type)
{
return (a.Type is FileStructType.Directory) ? -1 : 1;
}
return -a.FileSize.CompareTo(b.FileSize);
}
private static int SortByDateDesc(FileStruct a, FileStruct b)
{
switch (a.FileName, b.FileName)
{
case ("..", ".."): return 0;
case ("..", _): return -1;
case (_, ".."): return 1;
}
if (a.Type != b.Type)
{
return (a.Type == FileStructType.Directory) ? 1 : -1;
}
return string.Compare(a.FileModifiedDate, b.FileModifiedDate, StringComparison.CurrentCulture);
}
private static int SortByDateAsc(FileStruct a, FileStruct b)
{
switch (a.FileName, b.FileName)
{
case ("..", ".."): return 0;
case ("..", _): return -1;
case (_, ".."): return 1;
}
if (a.Type != b.Type)
{
return (a.Type == FileStructType.Directory) ? -1 : 1;
}
return -string.Compare(a.FileModifiedDate, b.FileModifiedDate, StringComparison.CurrentCulture);
}
private bool CreateDir(string dirPath)
{
var newPath = Path.Combine(this.currentPath, dirPath);
if (string.IsNullOrEmpty(newPath))
{
return false;
}
Directory.CreateDirectory(newPath);
return true;
}
private void ScanDir(string path)
{
if (!Directory.Exists(path))
{
return;
}
if (this.pathDecomposition.Count == 0)
{
this.SetCurrentDir(path);
}
if (this.pathDecomposition.Count > 0)
{
this.files.Clear();
if (this.pathDecomposition.Count > 1)
{
this.files.Add(new FileStruct
{
Type = FileStructType.Directory,
FilePath = path,
FileName = "..",
FileSize = 0,
FileModifiedDate = string.Empty,
FormattedFileSize = string.Empty,
Ext = string.Empty,
});
}
var dirInfo = new DirectoryInfo(path);
var dontShowHidden = this.flags.HasFlag(ImGuiFileDialogFlags.DontShowHiddenFiles);
foreach (var dir in dirInfo.EnumerateDirectories().OrderBy(d => d.Name))
{
if (string.IsNullOrEmpty(dir.Name))
{
continue;
}
if (dontShowHidden && dir.Name[0] == '.')
{
continue;
}
this.files.Add(GetDir(dir, path));
}
foreach (var file in dirInfo.EnumerateFiles().OrderBy(f => f.Name))
{
if (string.IsNullOrEmpty(file.Name))
{
continue;
}
if (dontShowHidden && file.Name[0] == '.')
{
continue;
}
if (!string.IsNullOrEmpty(file.Extension))
{
var ext = file.Extension;
if (this.filters.Count > 0 && !this.selectedFilter.Empty() && !this.selectedFilter.FilterExists(ext) && this.selectedFilter.Filter != ".*")
{
continue;
}
}
this.files.Add(GetFile(file, path));
}
this.SortFields(this.currentSortingField);
}
}
private IEnumerable GetDrives()
{
return this.driveListLoader.Drives.Select(drive => new SideBarItem(drive.Name, drive.Name, FontAwesomeIcon.Server));
}
private void SetupSideBar()
{
_ = this.driveListLoader.LoadDrivesAsync();
var personal = Path.GetDirectoryName(Environment.GetFolderPath(Environment.SpecialFolder.Personal));
this.quickAccess.Add(new SideBarItem("Desktop", Environment.GetFolderPath(Environment.SpecialFolder.Desktop), FontAwesomeIcon.Desktop));
this.quickAccess.Add(new SideBarItem("Documents", Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), FontAwesomeIcon.File));
if (!string.IsNullOrEmpty(personal))
{
this.quickAccess.Add(new SideBarItem("Downloads", Path.Combine(personal, "Downloads"), FontAwesomeIcon.Download));
}
this.quickAccess.Add(new SideBarItem("Favorites", Environment.GetFolderPath(Environment.SpecialFolder.Favorites), FontAwesomeIcon.Star));
this.quickAccess.Add(new SideBarItem("Music", Environment.GetFolderPath(Environment.SpecialFolder.MyMusic), FontAwesomeIcon.Music));
this.quickAccess.Add(new SideBarItem("Pictures", Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), FontAwesomeIcon.Image));
this.quickAccess.Add(new SideBarItem("Videos", Environment.GetFolderPath(Environment.SpecialFolder.MyVideos), FontAwesomeIcon.Video));
}
private SortingField GetNewSorting(int column)
=> column switch
{
0 when this.currentSortingField is SortingField.FileName => SortingField.FileNameDescending,
0 => SortingField.FileName,
1 when this.currentSortingField is SortingField.Type => SortingField.TypeDescending,
1 => SortingField.Type,
2 when this.currentSortingField is SortingField.Size => SortingField.SizeDescending,
2 => SortingField.Size,
3 when this.currentSortingField is SortingField.Date => SortingField.DateDescending,
3 => SortingField.Date,
_ => SortingField.None,
};
}