-
-
Notifications
You must be signed in to change notification settings - Fork 657
Implement move to recycle bin feature #4648
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,9 @@ | |||||||||||||||||
| using System.IO; | ||||||||||||||||||
| using System.Linq; | ||||||||||||||||||
| using System.Reflection; | ||||||||||||||||||
| using System.Runtime.InteropServices; | ||||||||||||||||||
| using Windows.Win32; | ||||||||||||||||||
| using Windows.Win32.UI.Shell; | ||||||||||||||||||
|
|
||||||||||||||||||
| #pragma warning disable IDE0005 | ||||||||||||||||||
| using System.Windows; | ||||||||||||||||||
|
|
@@ -275,6 +278,35 @@ | |||||||||||||||||
| return LocationExists(path) || FileExists(path); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// <summary> | ||||||||||||||||||
| /// Moves the specified file or directory to the Recycle Bin. | ||||||||||||||||||
| /// </summary> | ||||||||||||||||||
| /// <param name="path">The path of the file or directory to move.</param> | ||||||||||||||||||
| /// <exception cref="OperationCanceledException">Thrown when the operation is canceled.</exception> | ||||||||||||||||||
| public static void MoveToRecycleBin(this string path) | ||||||||||||||||||
| { | ||||||||||||||||||
| var shellItemGuid = typeof(IShellItem).GUID; | ||||||||||||||||||
| PInvoke.SHCreateItemFromParsingName(path, null, in shellItemGuid, out var shellItemObject).ThrowOnFailure(); | ||||||||||||||||||
|
Check failure on line 289 in Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
|
||||||||||||||||||
| var shellItem = (IShellItem)shellItemObject; | ||||||||||||||||||
|
|
||||||||||||||||||
| var fileOperation = new FileOperation(); | ||||||||||||||||||
| try | ||||||||||||||||||
| { | ||||||||||||||||||
| ((IFileOperation)fileOperation).SetOperationFlags(FILEOPERATION_FLAGS.FOF_ALLOWUNDO | FILEOPERATION_FLAGS.FOF_NOCONFIRMATION | FILEOPERATION_FLAGS.FOF_NOERRORUI); | ||||||||||||||||||
| ((IFileOperation)fileOperation).DeleteItem(shellItem, null); | ||||||||||||||||||
| ((IFileOperation)fileOperation).PerformOperations(); | ||||||||||||||||||
| ((IFileOperation)fileOperation).GetAnyOperationsAborted(out var aborted); | ||||||||||||||||||
|
Comment on lines
+295
to
+298
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: When the shell operation fails, Prompt for AI agents
Suggested change
|
||||||||||||||||||
|
|
||||||||||||||||||
| if (aborted) | ||||||||||||||||||
| throw new OperationCanceledException(); | ||||||||||||||||||
| } | ||||||||||||||||||
| finally | ||||||||||||||||||
| { | ||||||||||||||||||
| Marshal.FinalReleaseComObject(fileOperation); | ||||||||||||||||||
| Marshal.FinalReleaseComObject(shellItem); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// <summary> | ||||||||||||||||||
| /// Open a directory window (using the OS's default handler, usually explorer) | ||||||||||||||||||
| /// </summary> | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -182,28 +182,39 @@ public List<Result> LoadContextMenus(Result selectedResult) | |
| contextMenus.Add(new Result | ||
| { | ||
| Title = Localize.plugin_explorer_deletefilefolder(), | ||
| SubTitle = isFile ? Localize.plugin_explorer_deletefile_subtitle(): Localize.plugin_explorer_deletefolder_subtitle(), | ||
| SubTitle = Settings.DeleteToRecycleBin | ||
| ? Localize.plugin_explorer_move_to_recycle_bin_subtitle() | ||
| : isFile ? Localize.plugin_explorer_deletefile_subtitle() : Localize.plugin_explorer_deletefolder_subtitle(), | ||
| Action = (context) => | ||
| { | ||
| try | ||
| { | ||
| if (Context.API.ShowMsgBox( | ||
| Localize.plugin_explorer_delete_folder_link(record.FullPath), | ||
| if (Settings.ConfirmBeforeDeleting && Context.API.ShowMsgBox( | ||
| Settings.DeleteToRecycleBin | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: If Prompt for AI agents |
||
| ? Localize.plugin_explorer_move_to_recycle_bin_confirmation(record.FullPath) | ||
| : Localize.plugin_explorer_delete_folder_link(record.FullPath), | ||
| Localize.plugin_explorer_deletefilefolder(), | ||
| MessageBoxButton.OKCancel, | ||
| MessageBoxImage.Warning) | ||
| == MessageBoxResult.Cancel) | ||
| return false; | ||
|
|
||
| if (isFile) | ||
| if (Settings.DeleteToRecycleBin) | ||
| record.FullPath.MoveToRecycleBin(); | ||
| else if (isFile) | ||
| File.Delete(record.FullPath); | ||
| else | ||
| Directory.Delete(record.FullPath, true); | ||
|
|
||
| _ = Task.Run(() => | ||
| { | ||
| Context.API.ShowMsg(Localize.plugin_explorer_deletefilefoldersuccess(), | ||
| Localize.plugin_explorer_deletefilefoldersuccess_detail(record.FullPath), | ||
| Context.API.ShowMsg( | ||
| Settings.DeleteToRecycleBin | ||
| ? Localize.plugin_explorer_move_to_recycle_bin_success() | ||
| : Localize.plugin_explorer_deletefilefoldersuccess(), | ||
| Settings.DeleteToRecycleBin | ||
| ? Localize.plugin_explorer_move_to_recycle_bin_success_detail(record.FullPath) | ||
| : Localize.plugin_explorer_deletefilefoldersuccess_detail(record.FullPath), | ||
| Constants.ExplorerIconImageFullPath); | ||
| }); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🔎 Supported by static analysis
🤖 get_repo_knowledge executed:
get_repo_knowledge Flow-Launcher/Flow.Launcher /tmp/coderabbit-repo-knowledge/flow-launcher-flow-launcher-f497e4db/architectureLength of output: 30805
🏁 Script executed:
Repository: Flow-Launcher/Flow.Launcher
Length of output: 5153
🏁 Script executed:
Repository: Flow-Launcher/Flow.Launcher
Length of output: 44243
🌐 Web query:
site:learn.microsoft.com/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifileoperation-setoperationflags FOFX_RECYCLEONDELETE FOF_ALLOWUNDO💡 Result:
For
IFileOperation::SetOperationFlags:FOF_ALLOWUNDO(0x0040) — Preserve undo information when possible. For delete operations, this normally sends the item to the Recycle Bin. It is defined inShellapi.h. [1][2]FOFX_RECYCLEONDELETE(0x00080000) — Explicitly send deleted files to the Recycle Bin instead of permanently deleting them. Introduced in Windows 8; defined inShobjidl.h. [1]Example:
FOFX_RECYCLEONDELETEis the more explicit recycle-bin request;FOF_ALLOWUNDOalso enables undo support. If no flags are set,IFileOperationdefaults toFOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR. [1][1] (learn.microsoft.com)
[2] (learn.microsoft.com)
Citations:
🌐 Web query:
site:learn.microsoft.com FOFX_RECYCLEONDELETE cannot be recycled permanently deleted failure IFileOperation💡 Result:
The FOFX_RECYCLEONDELETE flag is a member of the IFileOperation operation flags, introduced in Windows 8, designed to send files to the Recycle Bin when a delete operation is performed [1][2]. If you are encountering issues where files are still being permanently deleted instead of being recycled, it is likely due to the environment or configuration rather than a failure of the flag itself [3][4]. Common reasons for permanent deletion despite using FOFX_RECYCLEONDELETE include: 1. Non-Local File Systems: Files on network drives, UNC paths, removable media (e.g., USB drives), or specific redirected locations (like certain DFS configurations) generally do not support the Recycle Bin [3][4]. When you delete items from these locations, they are typically permanently removed regardless of the flags used [3][4]. 2. Recycle Bin Settings: If the Recycle Bin for the specific drive is disabled, or if the file size exceeds the maximum storage capacity of the Recycle Bin, the system may bypass it and delete the file permanently [3]. 3. Operation Context: Ensure that the file paths provided to IFileOperation are fully qualified. Using relative paths can lead to unexpected behavior in Shell operations [5][6]. 4. UI/Icon Refresh: If the operation succeeds but the Recycle Bin icon does not update to show it is full, this is a separate issue from the deletion itself. You may need to manually refresh the Recycle Bin view, for example, by calling IShellView::Refresh on the Recycle Bin's view object after PerformOperations completes [7][8]. If you are debugging, ensure your IFileOperation::SetOperationFlags call is correctly setting the flag before PerformOperations is invoked [7][9][8]. Also, verify that the items being deleted are on a local NTFS/FAT volume that supports the Recycle Bin. If the operation returns a success code (S_OK) but the file is gone, the shell is functioning as expected for that file system path [7][9].
Citations:
Do not permanently delete items when Recycle Bin mode is enabled.
MoveToRecycleBinusesFOF_ALLOWUNDO, which recycles items only when possible. Unsupported locations or Recycle Bin settings can still cause permanent deletion, whileContextMenureports success. AddFOFX_RECYCLEONDELETEand reject or otherwise detect paths that cannot be recycled before reporting success.🤖 Prompt for AI Agents