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:
authorRobert Guetzkow <rjg>2019-10-10 11:53:13 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2019-10-10 18:30:57 +0300
commit8825250f5a85c0c16e74ed144dd2b4a7d752042f (patch)
tree906a7ecabd783ed2780fdb74e4977cd390cdf6ca /source/blender
parent826db891abe810d611922130cfd6c29279c11c11 (diff)
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
Diffstat (limited to 'source/blender')
-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
3 files changed, 205 insertions, 7 deletions
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 */