[Bf-blender-cvs] [8825250f5a8] master: File Browser: add back Delete, which now moves files to the trash

Robert Guetzkow noreply at git.blender.org
Thu Oct 10 17:35:19 CEST 2019


Commit: 8825250f5a85c0c16e74ed144dd2b4a7d752042f
Author: Robert Guetzkow
Date:   Thu Oct 10 10:53:13 2019 +0200
Branches: master
https://developer.blender.org/rB8825250f5a85c0c16e74ed144dd2b4a7d752042f

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

===================================================================

M	release/scripts/presets/keyconfig/keymap_data/blender_default.py
M	release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
M	release/scripts/startup/bl_ui/space_filebrowser.py
M	source/blender/blenlib/BLI_fileops.h
M	source/blender/blenlib/intern/fileops.c
M	source/blender/editors/space_file/file_ops.c

===================================================================

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)

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list