Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py2
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py3
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py8
-rw-r--r--source/blender/blenlib/BLI_fileops.h1
-rw-r--r--source/blender/blenlib/intern/fileops.c193
-rw-r--r--source/blender/editors/space_file/file_ops.c18
6 files changed, 217 insertions, 8 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 4a285ea2bad..0728cbf8f9f 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -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'},
diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
index 7427db6ebe7..c5c1bfa7f7b 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -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')]}),
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index a322b96f9dd..e1097e8d512 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -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)
diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h
index d78f167a8fd..bdf7588291f 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -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();
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index 99149f5ea42..3a45989fb63 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -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));
@@ -770,6 +948,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?
*/
static bool check_the_same(const char *path_a, const char *path_b)
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index b4b51de302d..5ea95383892 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -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 */