From 63d4a05616f7174164ff0a15c8fccd6f2c81947b Mon Sep 17 00:00:00 2001 From: xavierdono Date: Thu, 3 Jul 2025 17:30:10 +0200 Subject: [PATCH 1/4] 977 --- src/Commands/SaveDiscardOnTrash.cs | 51 +++++++++++++++++++++++ src/Commands/SaveDiscardOnTrashWindows.cs | 50 ++++++++++++++++++++++ src/Resources/Locales/es_ES.axaml | 1 + src/Resources/Locales/fr_FR.axaml | 1 + src/ViewModels/Discard.cs | 3 ++ src/ViewModels/Preferences.cs | 7 ++++ src/Views/Preferences.axaml | 7 +++- 7 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 src/Commands/SaveDiscardOnTrash.cs create mode 100644 src/Commands/SaveDiscardOnTrashWindows.cs diff --git a/src/Commands/SaveDiscardOnTrash.cs b/src/Commands/SaveDiscardOnTrash.cs new file mode 100644 index 000000000..2486a3933 --- /dev/null +++ b/src/Commands/SaveDiscardOnTrash.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace SourceGit.Commands +{ + public static class SaveDiscardOnTrash + { + public static async Task ProcessSaveDiscardOnTrash(string repo, List changes) + { + changes ??= await new QueryLocalChanges(repo).GetResultAsync().ConfigureAwait(false); + + var succ = false; + string fullName = $"discard_{DateTime.Now.Ticks}.patch"; + string trashDirectory; + + switch (Environment.OSVersion.Platform) + { + case PlatformID.Unix: + trashDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "Trash", "files"); + fullName = Path.Combine(trashDirectory, fullName); + succ = await SaveChangesAsPatch.ProcessLocalChangesAsync(repo, changes, true, fullName); + if(succ) return false; + + break; + case PlatformID.MacOSX: + trashDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".Trash"); + fullName = Path.Combine(trashDirectory, fullName); + succ = await SaveChangesAsPatch.ProcessLocalChangesAsync(repo, changes, true, fullName); + if (succ) + return false; + + break; + default: + trashDirectory = Path.GetTempPath(); + fullName = Path.Combine(trashDirectory, fullName); + succ = await SaveChangesAsPatch.ProcessLocalChangesAsync(repo, changes, true, fullName); + if (succ) + return false; + + if (!SaveDiscardOnTrashWindows.MoveFileToTrash(fullName)) + return false; + + break; + } + + return false; + } + } +} diff --git a/src/Commands/SaveDiscardOnTrashWindows.cs b/src/Commands/SaveDiscardOnTrashWindows.cs new file mode 100644 index 000000000..57870d40b --- /dev/null +++ b/src/Commands/SaveDiscardOnTrashWindows.cs @@ -0,0 +1,50 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace SourceGit.Commands +{ + public static class SaveDiscardOnTrashWindows + { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct SHFILEOPSTRUCT + { + public IntPtr hwnd; + public uint wFunc; + public string pFrom; + public string pTo; + public ushort fFlags; + public bool fAnyOperationsAborted; + public IntPtr hNameMappings; + public string lpszProgressTitle; + } + + private const uint FO_DELETE = 0x0003; // Delete operation + private const uint FOF_ALLOWUNDO = 0x0040; // Put in Trash + private const uint FOF_NOCONFIRMATION = 0x0010; // No confirmation + + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + private static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp); + + public static bool MoveFileToTrash(string file) + { + SHFILEOPSTRUCT fileOp = new SHFILEOPSTRUCT + { + wFunc = FO_DELETE, + pFrom = file + '\0', + pTo = null, + fFlags = (ushort)(FOF_ALLOWUNDO | FOF_NOCONFIRMATION), + fAnyOperationsAborted = false, + hNameMappings = IntPtr.Zero, + lpszProgressTitle = null + }; + + bool result = SHFileOperation(ref fileOp) == 0; + + if (result) + File.Delete(file); + + return result; + } + } +} diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 3cc70b32c..b9c41da8f 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -527,6 +527,7 @@ Formato de Fecha Idioma Commits en el historial + Save discard in Trash Mostrar hora del autor en lugar de la hora del commit en el gráfico Mostrar hijos en los detalles de commit Mostrar etiquetas en el gráfico de commit diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index 10e9bc14b..24fe5cff8 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -472,6 +472,7 @@ Format de date Language Historique de commits + Enregistre les rejets dans la Corbeille Afficher l'heure de l'auteur au lieu de l'heure de validation dans le graphique Afficher les enfants dans les détails du commit Afficher les tags dans le graphique des commits diff --git a/src/ViewModels/Discard.cs b/src/ViewModels/Discard.cs index 1fcda9049..8f9795ce8 100644 --- a/src/ViewModels/Discard.cs +++ b/src/ViewModels/Discard.cs @@ -64,6 +64,9 @@ public override async Task Sure() var log = _repo.CreateLog("Discard all"); Use(log); + if (Preferences.Instance.DiscardChangedOnBin) + await Commands.SaveDiscardOnTrash.ProcessSaveDiscardOnTrash(_repo.FullPath, _changes); + if (Mode is DiscardAllMode all) await Commands.Discard.AllAsync(_repo.FullPath, all.IncludeIgnored, log); else diff --git a/src/ViewModels/Preferences.cs b/src/ViewModels/Preferences.cs index d7fe73f90..f3b588a13 100644 --- a/src/ViewModels/Preferences.cs +++ b/src/ViewModels/Preferences.cs @@ -158,6 +158,12 @@ public bool Check4UpdatesOnStartup set => SetProperty(ref _check4UpdatesOnStartup, value); } + public bool DiscardChangedOnBin + { + get => _discardChangedOnBin; + set => SetProperty(ref _discardChangedOnBin, value); + } + public bool ShowAuthorTimeInGraph { get => _showAuthorTimeInGraph; @@ -682,6 +688,7 @@ private bool RemoveInvalidRepositoriesRecursive(List collection) private bool _check4UpdatesOnStartup = true; private double _lastCheckUpdateTime = 0; private string _ignoreUpdateTag = string.Empty; + private bool _discardChangedOnBin = false; private bool _showTagsInGraph = true; private bool _useTwoColumnsLayoutInHistories = false; diff --git a/src/Views/Preferences.axaml b/src/Views/Preferences.axaml index bf5c29023..4728c2a95 100644 --- a/src/Views/Preferences.axaml +++ b/src/Views/Preferences.axaml @@ -46,7 +46,7 @@ - + + + From 581e3295ea9108c9d56c2b0745288408772c1fef Mon Sep 17 00:00:00 2001 From: xavierdono Date: Thu, 3 Jul 2025 17:40:43 +0200 Subject: [PATCH 2/4] Fix Trad --- src/Resources/Locales/en_US.axaml | 1 + src/Resources/Locales/es_ES.axaml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 38a65f637..1779cc0a1 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -526,6 +526,7 @@ Date Format Language History Commits + Save discard in Trash Show author time instead of commit time in graph Show children in the commit details Show tags in commit graph diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index b9c41da8f..3cc70b32c 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -527,7 +527,6 @@ Formato de Fecha Idioma Commits en el historial - Save discard in Trash Mostrar hora del autor en lugar de la hora del commit en el gráfico Mostrar hijos en los detalles de commit Mostrar etiquetas en el gráfico de commit From 66442c45b6fe624a52518b131369ac3450b52d80 Mon Sep 17 00:00:00 2001 From: xavierdono Date: Fri, 4 Jul 2025 22:03:20 +0200 Subject: [PATCH 3/4] Fix Check OS: Use build it solution --- src/Commands/SaveDiscardOnTrash.cs | 51 ++++++++++++++---------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/Commands/SaveDiscardOnTrash.cs b/src/Commands/SaveDiscardOnTrash.cs index 2486a3933..25856b5a8 100644 --- a/src/Commands/SaveDiscardOnTrash.cs +++ b/src/Commands/SaveDiscardOnTrash.cs @@ -15,34 +15,31 @@ public static async Task ProcessSaveDiscardOnTrash(string repo, List Date: Fri, 4 Jul 2025 22:24:58 +0200 Subject: [PATCH 4/4] Fix Move File To Trash: Use build it solution --- src/Commands/SaveDiscardOnTrash.cs | 36 ++++------------ src/Commands/SaveDiscardOnTrashWindows.cs | 50 ----------------------- src/Native/Linux.cs | 5 +++ src/Native/MacOS.cs | 5 +++ src/Native/OS.cs | 6 +++ src/Native/Windows.cs | 42 +++++++++++++++++++ 6 files changed, 66 insertions(+), 78 deletions(-) delete mode 100644 src/Commands/SaveDiscardOnTrashWindows.cs diff --git a/src/Commands/SaveDiscardOnTrash.cs b/src/Commands/SaveDiscardOnTrash.cs index 25856b5a8..51d4ecd04 100644 --- a/src/Commands/SaveDiscardOnTrash.cs +++ b/src/Commands/SaveDiscardOnTrash.cs @@ -11,36 +11,16 @@ public static async Task ProcessSaveDiscardOnTrash(string repo, List FindExternalTools(); + bool MoveFileToTrash(string file); void OpenTerminal(string workdir); void OpenInFileManager(string path, bool select); @@ -162,6 +163,11 @@ public static void OpenBrowser(string url) _backend.OpenBrowser(url); } + public static bool MoveFileToTrash(string file) + { + return _backend.MoveFileToTrash(file); + } + public static void OpenTerminal(string workdir) { if (string.IsNullOrEmpty(ShellOrTerminal)) diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index caccc5e3c..15f78f8c6 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -33,6 +33,19 @@ internal struct MARGINS public int cyBottomHeight; } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + internal struct SHFILEOPSTRUCT + { + public IntPtr hwnd; + public uint wFunc; + public string pFrom; + public string pTo; + public ushort fFlags; + public bool fAnyOperationsAborted; + public IntPtr hNameMappings; + public string lpszProgressTitle; + } + [DllImport("dwmapi.dll")] private static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins); @@ -48,6 +61,9 @@ internal struct MARGINS [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)] private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, int cild, IntPtr apidl, int dwFlags); + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + private static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp); + [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); @@ -253,6 +269,28 @@ public void OpenWithDefaultEditor(string file) Process.Start(start); } + public bool MoveFileToTrash(string file) + { + SHFILEOPSTRUCT fileOp = new SHFILEOPSTRUCT + { + wFunc = FO_DELETE, + pFrom = file + '\0', + pTo = null, + fFlags = (ushort)(FOF_ALLOWUNDO | FOF_NOCONFIRMATION), + fAnyOperationsAborted = false, + hNameMappings = IntPtr.Zero, + lpszProgressTitle = null + }; + + // Move the file in the Trash + bool result = SHFileOperation(ref fileOp) == 0; + + // If move ok delete the temp file + if (result) File.Delete(file); + + return result; + } + private void FixWindowFrameOnWin10(Window w) { // Schedule the DWM frame extension to run in the next render frame @@ -431,5 +469,9 @@ private string FindVSSolutionFile(DirectoryInfo dir, int leftDepth) return null; } + + private const uint FO_DELETE = 0x0003; // Delete operation + private const uint FOF_ALLOWUNDO = 0x0040; // Put in Trash + private const uint FOF_NOCONFIRMATION = 0x0010; // No confirmation } }