File Browser: add back Delete, which now moves files to the trash
In Blender 2.7 delete would permanently delete files, now this function is back but using more standard behavior. This patch includes code contributed by Kris (Metricity). Differential Revision: https://developer.blender.org/D4585
This commit is contained in:
parent
826db891ab
commit
8825250f5a
Notes:
blender-bot
2024-01-16 18:05:25 +01:00
Referenced by issue #97338, Incorrect handling of reference counting for COM Referenced by issue #88131, Blender Delete file | problem Referenced by issue #64104, Make Delete in the File Browser use the system Trash
|
@ -1793,6 +1793,8 @@ def km_file_browser(params):
|
|||
{"properties": [("data_path", 'space_data.params.show_hidden')]}),
|
||||
("file.directory_new", {"type": 'I', "value": 'PRESS'},
|
||||
{"properties": [("confirm", False)]}),
|
||||
("file.delete", {"type": 'X', "value": 'PRESS'}, None),
|
||||
("file.delete", {"type": 'DEL', "value": 'PRESS'}, None),
|
||||
("file.smoothscroll", {"type": 'TIMER1', "value": 'ANY', "any": True}, None),
|
||||
("file.bookmark_add", {"type": 'B', "value": 'PRESS', "ctrl": True}, None),
|
||||
("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS'},
|
||||
|
|
|
@ -1177,10 +1177,13 @@ def km_file_browser(params):
|
|||
("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True}, None),
|
||||
("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True}, None),
|
||||
("file.refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
|
||||
("file.previous", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
|
||||
("file.next", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, None),
|
||||
("wm.context_toggle", {"type": 'H', "value": 'PRESS'},
|
||||
{"properties": [("data_path", 'space_data.params.show_hidden')]}),
|
||||
("file.directory_new", {"type": 'I', "value": 'PRESS'},
|
||||
{"properties": [("confirm", False)]}),
|
||||
("file.delete", {"type": 'DEL', "value": 'PRESS'}, None),
|
||||
("file.smoothscroll", {"type": 'TIMER1', "value": 'ANY', "any": True}, None),
|
||||
("wm.context_toggle", {"type": 'T', "value": 'PRESS'},
|
||||
{"properties": [("data_path", 'space_data.show_region_toolbar')]}),
|
||||
|
|
|
@ -469,7 +469,12 @@ class FILEBROWSER_MT_context_menu(Menu):
|
|||
layout.separator()
|
||||
|
||||
layout.operator("file.rename", text="Rename")
|
||||
# layout.operator("file.delete")
|
||||
sub = layout.row()
|
||||
sub.operator_context = 'EXEC_DEFAULT'
|
||||
sub.operator("file.delete", text="Delete")
|
||||
|
||||
layout.separator()
|
||||
|
||||
sub = layout.row()
|
||||
sub.operator_context = 'EXEC_DEFAULT'
|
||||
sub.operator("file.directory_new", text="New Folder")
|
||||
|
@ -503,5 +508,6 @@ classes = (
|
|||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
|
|
@ -50,6 +50,7 @@ int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
|||
int BLI_copy(const char *path, const char *to) ATTR_NONNULL();
|
||||
int BLI_rename(const char *from, const char *to) ATTR_NONNULL();
|
||||
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL();
|
||||
int BLI_delete_soft(const char *path, const char **error_message) ATTR_NONNULL();
|
||||
#if 0 /* Unused */
|
||||
int BLI_move(const char *path, const char *to) ATTR_NONNULL();
|
||||
int BLI_create_symlink(const char *path, const char *to) ATTR_NONNULL();
|
||||
|
|
|
@ -33,16 +33,24 @@
|
|||
#include "zlib.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# include <windows.h>
|
||||
# include <shellapi.h>
|
||||
# include <shobjidl.h>
|
||||
# include <io.h>
|
||||
# include "BLI_winstuff.h"
|
||||
# include "BLI_fileops_types.h"
|
||||
# include "utf_winfunc.h"
|
||||
# include "utfconv.h"
|
||||
#else
|
||||
# if defined(__APPLE__)
|
||||
# include <CoreFoundation/CoreFoundation.h>
|
||||
# include <objc/runtime.h>
|
||||
# include <objc/message.h>
|
||||
# endif
|
||||
# include <sys/param.h>
|
||||
# include <dirent.h>
|
||||
# include <unistd.h>
|
||||
# include <sys/stat.h>
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
@ -288,6 +296,64 @@ int BLI_access(const char *filename, int mode)
|
|||
return uaccess(filename, mode);
|
||||
}
|
||||
|
||||
static bool delete_soft(const wchar_t *path_16, const char **error_message)
|
||||
{
|
||||
/* Deletes file or directory to recycling bin. The latter moves all contained files and
|
||||
* directories recursively to the recycling bin as well. */
|
||||
IFileOperation *pfo;
|
||||
IShellItem *pSI;
|
||||
|
||||
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
*error_message = "Failed to initialize COM";
|
||||
goto error_1;
|
||||
}
|
||||
|
||||
hr = CoCreateInstance(
|
||||
&CLSID_FileOperation, NULL, CLSCTX_ALL, &IID_IFileOperation, (void **)&pfo);
|
||||
if (FAILED(hr)) {
|
||||
*error_message = "Failed to create FileOperation instance";
|
||||
goto error_2;
|
||||
}
|
||||
|
||||
/* Flags for deletion:
|
||||
* FOF_ALLOWUNDO: Enables moving file to recycling bin.
|
||||
* FOF_SILENT: Don't show progress dialog box.
|
||||
* FOF_WANTNUKEWARNING: Show dialog box if file can't be moved to recycling bin. */
|
||||
hr = pfo->lpVtbl->SetOperationFlags(pfo, FOF_ALLOWUNDO | FOF_SILENT | FOF_WANTNUKEWARNING);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
*error_message = "Failed to set operation flags";
|
||||
goto error_2;
|
||||
}
|
||||
|
||||
hr = SHCreateItemFromParsingName(path_16, NULL, &IID_IShellItem, (void **)&pSI);
|
||||
if (FAILED(hr)) {
|
||||
*error_message = "Failed to parse path";
|
||||
goto error_2;
|
||||
}
|
||||
|
||||
hr = pfo->lpVtbl->DeleteItem(pfo, pSI, NULL);
|
||||
if (FAILED(hr)) {
|
||||
*error_message = "Failed to prepare delete operation";
|
||||
goto error_2;
|
||||
}
|
||||
|
||||
hr = pfo->lpVtbl->PerformOperations(pfo);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
*error_message = "Failed to delete file or directory";
|
||||
}
|
||||
|
||||
error_2:
|
||||
pfo->lpVtbl->Release(pfo);
|
||||
CoUninitialize(); /* Has to be uninitialized when CoInitializeEx returns either S_OK or S_FALSE
|
||||
*/
|
||||
error_1:
|
||||
return FAILED(hr);
|
||||
}
|
||||
|
||||
static bool delete_unique(const char *path, const bool dir)
|
||||
{
|
||||
bool err;
|
||||
|
@ -370,6 +436,24 @@ int BLI_delete(const char *file, bool dir, bool recursive)
|
|||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the files or directories to the recycling bin.
|
||||
*/
|
||||
int BLI_delete_soft(const char *file, const char **error_message)
|
||||
{
|
||||
int err;
|
||||
|
||||
BLI_assert(!BLI_path_is_rel(file));
|
||||
|
||||
UTF16_ENCODE(file);
|
||||
|
||||
err = delete_soft(file_16, error_message);
|
||||
|
||||
UTF16_UN_ENCODE(file);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Not used anywhere! */
|
||||
# if 0
|
||||
int BLI_move(const char *file, const char *to)
|
||||
|
@ -720,6 +804,100 @@ static int delete_single_file(const char *from, const char *UNUSED(to))
|
|||
return RecursiveOp_Callback_OK;
|
||||
}
|
||||
|
||||
# ifdef __APPLE__
|
||||
static int delete_soft(const char *file, const char **error_message)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
Class NSAutoreleasePoolClass = objc_getClass("NSAutoreleasePool");
|
||||
SEL allocSel = sel_registerName("alloc");
|
||||
SEL initSel = sel_registerName("init");
|
||||
id poolAlloc = ((id(*)(Class, SEL))objc_msgSend)(NSAutoreleasePoolClass, allocSel);
|
||||
id pool = ((id(*)(id, SEL))objc_msgSend)(poolAlloc, initSel);
|
||||
|
||||
Class NSStringClass = objc_getClass("NSString");
|
||||
SEL stringWithUTF8StringSel = sel_registerName("stringWithUTF8String:");
|
||||
id pathString = ((id(*)(Class, SEL, const char *))objc_msgSend)(
|
||||
NSStringClass, stringWithUTF8StringSel, file);
|
||||
|
||||
Class NSFileManagerClass = objc_getClass("NSFileManager");
|
||||
SEL defaultManagerSel = sel_registerName("defaultManager");
|
||||
id fileManager = ((id(*)(Class, SEL))objc_msgSend)(NSFileManagerClass, defaultManagerSel);
|
||||
|
||||
Class NSURLClass = objc_getClass("NSURL");
|
||||
SEL fileURLWithPathSel = sel_registerName("fileURLWithPath:");
|
||||
id nsurl = ((id(*)(Class, SEL, id))objc_msgSend)(NSURLClass, fileURLWithPathSel, pathString);
|
||||
|
||||
SEL trashItemAtURLSel = sel_registerName("trashItemAtURL:resultingItemURL:error:");
|
||||
BOOL deleteSuccessful = ((BOOL(*)(id, SEL, id, id, id))objc_msgSend)(
|
||||
fileManager, trashItemAtURLSel, nsurl, nil, nil);
|
||||
|
||||
if (deleteSuccessful) {
|
||||
ret = 0;
|
||||
}
|
||||
else {
|
||||
*error_message = "The Cocoa API call to delete file or directory failed";
|
||||
}
|
||||
|
||||
SEL drainSel = sel_registerName("drain");
|
||||
((void (*)(id, SEL))objc_msgSend)(pool, drainSel);
|
||||
|
||||
return ret;
|
||||
}
|
||||
# else
|
||||
static int delete_soft(const char *file, const char **error_message)
|
||||
{
|
||||
const char *args[5];
|
||||
const char *process_failed;
|
||||
|
||||
char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
|
||||
char *xdg_session_desktop = getenv("XDG_SESSION_DESKTOP");
|
||||
|
||||
if ((xdg_current_desktop != NULL && strcmp(xdg_current_desktop, "KDE") == 0) ||
|
||||
(xdg_session_desktop != NULL && strcmp(xdg_session_desktop, "KDE") == 0)) {
|
||||
args[0] = "kioclient5";
|
||||
args[1] = "move";
|
||||
args[2] = file;
|
||||
args[3] = "trash:/";
|
||||
args[4] = NULL;
|
||||
process_failed = "kioclient5 reported failure";
|
||||
}
|
||||
else {
|
||||
args[0] = "gio";
|
||||
args[1] = "trash";
|
||||
args[2] = file;
|
||||
args[3] = NULL;
|
||||
process_failed = "gio reported failure";
|
||||
}
|
||||
|
||||
int pid = fork();
|
||||
|
||||
if (pid != 0) {
|
||||
/* Parent process */
|
||||
int wstatus = 0;
|
||||
|
||||
waitpid(pid, &wstatus, 0);
|
||||
|
||||
if (!WIFEXITED(wstatus)) {
|
||||
*error_message =
|
||||
"Blender may not support moving files or directories to trash on your system.";
|
||||
return -1;
|
||||
}
|
||||
else if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus)) {
|
||||
*error_message = process_failed;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
execvp(args[0], (char **)args);
|
||||
|
||||
*error_message = "Forking process failed.";
|
||||
return -1; /* This should only be reached if execvp fails and stack isn't replaced. */
|
||||
}
|
||||
# endif
|
||||
|
||||
FILE *BLI_fopen(const char *filename, const char *mode)
|
||||
{
|
||||
BLI_assert(!BLI_path_is_rel(filename));
|
||||
|
@ -769,6 +947,19 @@ int BLI_delete(const char *file, bool dir, bool recursive)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft deletes the specified file or directory (depending on dir) by moving the files to the
|
||||
* recycling bin, optionally doing recursive delete of directory contents.
|
||||
*
|
||||
* \return zero on success (matching 'remove' behavior).
|
||||
*/
|
||||
int BLI_delete_soft(const char *file, const char **error_message)
|
||||
{
|
||||
BLI_assert(!BLI_path_is_rel(file));
|
||||
|
||||
return delete_soft(file, error_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the two paths denote the same file-system object?
|
||||
*/
|
||||
|
|
|
@ -2504,23 +2504,29 @@ int file_delete_exec(bContext *C, wmOperator *op)
|
|||
int numfiles = filelist_files_ensure(sfile->files);
|
||||
int i;
|
||||
|
||||
const char *error_message = NULL;
|
||||
bool report_error = false;
|
||||
errno = 0;
|
||||
for (i = 0; i < numfiles; i++) {
|
||||
if (filelist_entry_select_index_get(sfile->files, i, CHECK_FILES)) {
|
||||
file = filelist_file(sfile->files, i);
|
||||
BLI_make_file_string(BKE_main_blendfile_path(bmain), str, sfile->params->dir, file->relpath);
|
||||
if (BLI_delete(str, false, false) != 0 || BLI_exists(str)) {
|
||||
if (BLI_delete_soft(str, &error_message) != 0 || BLI_exists(str)) {
|
||||
report_error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (report_error) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Could not delete file: %s",
|
||||
errno ? strerror(errno) : "unknown error");
|
||||
if (error_message != NULL) {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "Could not delete file or directory: %s", error_message);
|
||||
}
|
||||
else {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_ERROR,
|
||||
"Could not delete file or directory: %s",
|
||||
errno ? strerror(errno) : "unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
ED_fileselect_clear(wm, sa, sfile);
|
||||
|
@ -2533,7 +2539,7 @@ void FILE_OT_delete(struct wmOperatorType *ot)
|
|||
{
|
||||
/* identifiers */
|
||||
ot->name = "Delete Selected Files";
|
||||
ot->description = "Delete selected files";
|
||||
ot->description = "Move selected files to the trash or recycle bin";
|
||||
ot->idname = "FILE_OT_delete";
|
||||
|
||||
/* api callbacks */
|
||||
|
|
Loading…
Reference in New Issue