macOS/File Browser: Support external operations #107267
|
@ -117,10 +117,13 @@ ENUM_OPERATORS(eFileAttributes, FILE_ATTR_HARDLINK);
|
|||
/** \name External File Operations
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Not all operations may be supported on all platforms.
|
||||
ankitm marked this conversation as resolved
Outdated
|
||||
*/
|
||||
typedef enum FileExternalOperation {
|
||||
FILE_EXTERNAL_OPERATION_OPEN = 1,
|
||||
FILE_EXTERNAL_OPERATION_FOLDER_OPEN,
|
||||
/* Following are Windows-only: */
|
||||
FILE_EXTERNAL_OPERATION_FILE_REVEAL,
|
||||
FILE_EXTERNAL_OPERATION_EDIT,
|
||||
FILE_EXTERNAL_OPERATION_NEW,
|
||||
FILE_EXTERNAL_OPERATION_FIND,
|
||||
|
@ -133,7 +136,7 @@ typedef enum FileExternalOperation {
|
|||
FILE_EXTERNAL_OPERATION_RUNAS,
|
||||
FILE_EXTERNAL_OPERATION_PROPERTIES,
|
||||
FILE_EXTERNAL_OPERATION_FOLDER_FIND,
|
||||
FILE_EXTERNAL_OPERATION_FOLDER_CMD,
|
||||
FILE_EXTERNAL_OPERATION_FOLDER_TERMINAL,
|
||||
} FileExternalOperation;
|
||||
|
||||
bool BLI_file_external_operation_supported(const char *filepath, FileExternalOperation operation);
|
||||
|
@ -357,6 +360,8 @@ void BLI_file_free_lines(struct LinkNode *lines);
|
|||
* Giving a path without leading `~` is not an error.
|
||||
*/
|
||||
const char *BLI_expand_tilde(const char *path_with_tilde);
|
||||
bool BLI_apple_external_operation_supported(const char *filepath, FileExternalOperation operation);
|
||||
bool BLI_apple_external_operation_execute(const char *filepath, FileExternalOperation operation);
|
||||
#endif
|
||||
/* This weirdo pops up in two places. */
|
||||
#if !defined(WIN32)
|
||||
|
|
|
@ -55,6 +55,8 @@ static char *windows_operation_string(FileExternalOperation operation)
|
|||
return "open";
|
||||
case FILE_EXTERNAL_OPERATION_FOLDER_OPEN:
|
||||
return "open";
|
||||
case FILE_EXTERNAL_OPERATION_FILE_REVEAL:
|
||||
return NULL;
|
||||
case FILE_EXTERNAL_OPERATION_EDIT:
|
||||
return "edit";
|
||||
case FILE_EXTERNAL_OPERATION_NEW:
|
||||
|
@ -79,11 +81,11 @@ static char *windows_operation_string(FileExternalOperation operation)
|
|||
return "properties";
|
||||
case FILE_EXTERNAL_OPERATION_FOLDER_FIND:
|
||||
return "find";
|
||||
case FILE_EXTERNAL_OPERATION_FOLDER_CMD:
|
||||
case FILE_EXTERNAL_OPERATION_FOLDER_TERMINAL:
|
||||
return "cmd";
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return "";
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -92,6 +94,8 @@ bool BLI_file_external_operation_supported(const char *filepath, FileExternalOpe
|
|||
#ifdef WIN32
|
||||
char *opstring = windows_operation_string(operation);
|
||||
return BLI_windows_external_operation_supported(filepath, opstring);
|
||||
#elif defined(__APPLE__)
|
||||
return BLI_apple_external_operation_supported(filepath, operation);
|
||||
#else
|
||||
UNUSED_VARS(filepath, operation);
|
||||
return false;
|
||||
|
@ -107,6 +111,8 @@ bool BLI_file_external_operation_execute(const char *filepath, FileExternalOpera
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
#elif defined(__APPLE__)
|
||||
return BLI_apple_external_operation_execute(filepath, operation);
|
||||
#else
|
||||
UNUSED_VARS(filepath, operation);
|
||||
return false;
|
||||
|
|
|
@ -7,10 +7,16 @@
|
|||
* macOS specific implementations for storage.c.
|
||||
*/
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <AppKit/NSPasteboard.h>
|
||||
#import <AppKit/NSWorkspace.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <string>
|
||||
#include <sys/xattr.h>
|
||||
#include <utility>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
@ -212,3 +218,146 @@ bool BLI_change_working_dir(const char *dir)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* \param service_invocation: Taken from `/System/Library/CoreServices/pbs -dump`
|
||||
* \param fileurl: The fileurl to operate on. Starts with `/`.
|
||||
* \return: If the service call succeeded.
|
||||
*/
|
||||
bool perform_service_for_fileurl(NSString *service_invocation, NSString *fileurl)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSPasteboard *pasteboard = [NSPasteboard pasteboardWithUniqueName];
|
||||
[pasteboard declareTypes:@[ NSPasteboardTypeString ] owner:nil];
|
||||
[pasteboard setString:fileurl forType:NSPasteboardTypeString];
|
||||
const bool ok = NSPerformService(service_invocation, pasteboard);
|
||||
[pasteboard releaseGlobally];
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
bool external_file_finder_open_default(const char *filepath)
|
||||
{
|
||||
@autoreleasepool {
|
||||
/* `perform_service_for_fileurl(@"Finder/Open"..` shows OS confirmation popup on
|
||||
* every call, so use a different method.
|
||||
*/
|
||||
|
||||
NSURL *url = [NSURL fileURLWithFileSystemRepresentation:filepath
|
||||
isDirectory:NO
|
||||
relativeToURL:nil];
|
||||
return [[NSWorkspace sharedWorkspace] openURL:url];
|
||||
}
|
||||
}
|
||||
|
||||
bool external_file_finder_reveal(const char *filepath)
|
||||
{
|
||||
@autoreleasepool {
|
||||
return perform_service_for_fileurl(@"Finder/Reveal", [NSString stringWithUTF8String:filepath]);
|
||||
}
|
||||
}
|
||||
|
||||
bool external_file_get_info(const char *filepath)
|
||||
{
|
||||
@autoreleasepool {
|
||||
return perform_service_for_fileurl(@"Finder/Show Info",
|
||||
[NSString stringWithUTF8String:filepath]);
|
||||
}
|
||||
}
|
||||
|
||||
bool external_file_open_terminal(const char *filepath)
|
||||
{
|
||||
@autoreleasepool {
|
||||
return perform_service_for_fileurl(@"New Terminal at Folder",
|
||||
[NSString stringWithUTF8String:filepath]);
|
||||
}
|
||||
}
|
||||
|
||||
using ExternalOperationExecutor = bool (*)(const char *);
|
||||
|
||||
ExternalOperationExecutor get_external_operation_executor(const char *filepath,
|
||||
FileExternalOperation operation)
|
||||
{
|
||||
switch (operation) {
|
||||
case FILE_EXTERNAL_OPERATION_OPEN: {
|
||||
return external_file_finder_open_default;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_FOLDER_OPEN: {
|
||||
return external_file_finder_reveal;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_FILE_REVEAL: {
|
||||
return external_file_finder_reveal;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_EDIT: {
|
||||
return nullptr;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_NEW: {
|
||||
return nullptr;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_FIND: {
|
||||
return nullptr;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_SHOW: {
|
||||
return nullptr;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_PLAY: {
|
||||
return nullptr;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_BROWSE: {
|
||||
return nullptr;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_PREVIEW: {
|
||||
return nullptr;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_PRINT: {
|
||||
return nullptr;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_INSTALL: {
|
||||
return nullptr;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_RUNAS: {
|
||||
return nullptr;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_PROPERTIES: {
|
||||
return external_file_get_info;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_FOLDER_FIND: {
|
||||
return nullptr;
|
||||
}
|
||||
case FILE_EXTERNAL_OPERATION_FOLDER_TERMINAL: {
|
||||
return external_file_open_terminal;
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool BLI_apple_external_operation_execute(const char *filepath, FileExternalOperation operation)
|
||||
{
|
||||
@autoreleasepool {
|
||||
const ExternalOperationExecutor executor = get_external_operation_executor(filepath,
|
||||
operation);
|
||||
if (executor == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return executor(filepath);
|
||||
}
|
||||
}
|
||||
bool BLI_apple_external_operation_supported(const char *filepath, FileExternalOperation operation)
|
||||
{
|
||||
@autoreleasepool {
|
||||
const ExternalOperationExecutor executor = get_external_operation_executor(filepath,
|
||||
operation);
|
||||
if (!executor) {
|
||||
return false;
|
||||
}
|
||||
if (operation == FILE_EXTERNAL_OPERATION_FILE_REVEAL && !BLI_is_file(filepath)) {
|
||||
return false;
|
||||
}
|
||||
if (operation == FILE_EXTERNAL_OPERATION_FOLDER_OPEN && !BLI_is_dir(filepath)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -203,6 +203,9 @@ static bool BLI_windows_file_operation_is_registered(const char *extension, cons
|
|||
|
||||
bool BLI_windows_external_operation_supported(const char *filepath, const char *operation)
|
||||
{
|
||||
if (!operation) {
|
||||
return false;
|
||||
}
|
||||
if (STREQ(operation, "open") || STREQ(operation, "properties")) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
* \ingroup spfile
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_appdir.h"
|
||||
#include "BKE_blendfile.h"
|
||||
|
@ -1775,33 +1775,75 @@ bool file_draw_check_exists(SpaceFile *sfile)
|
|||
* \{ */
|
||||
|
||||
static const EnumPropertyItem file_external_operation[] = {
|
||||
{FILE_EXTERNAL_OPERATION_OPEN, "OPEN", 0, "Open", "Open the file"},
|
||||
{FILE_EXTERNAL_OPERATION_FOLDER_OPEN, "FOLDER_OPEN", 0, "Open Folder", "Open the folder"},
|
||||
{FILE_EXTERNAL_OPERATION_EDIT, "EDIT", 0, "Edit", "Edit the file"},
|
||||
{FILE_EXTERNAL_OPERATION_NEW, "NEW", 0, "New", "Create a new file of this type"},
|
||||
{FILE_EXTERNAL_OPERATION_FIND, "FIND", 0, "Find File", "Search for files of this type"},
|
||||
{FILE_EXTERNAL_OPERATION_SHOW, "SHOW", 0, "Show", "Show this file"},
|
||||
{FILE_EXTERNAL_OPERATION_PLAY, "PLAY", 0, "Play", "Play this file"},
|
||||
{FILE_EXTERNAL_OPERATION_BROWSE, "BROWSE", 0, "Browse", "Browse this file"},
|
||||
{FILE_EXTERNAL_OPERATION_PREVIEW, "PREVIEW", 0, "Preview", "Preview this file"},
|
||||
{FILE_EXTERNAL_OPERATION_PRINT, "PRINT", 0, "Print", "Print this file"},
|
||||
{FILE_EXTERNAL_OPERATION_INSTALL, "INSTALL", 0, "Install", "Install this file"},
|
||||
{FILE_EXTERNAL_OPERATION_RUNAS, "RUNAS", 0, "Run As User", "Run as specific user"},
|
||||
{FILE_EXTERNAL_OPERATION_OPEN,
|
||||
"OPEN",
|
||||
ICON_NONE,
|
||||
"Open",
|
||||
"Open this file in its default application"},
|
||||
ankitm marked this conversation as resolved
Outdated
Julian Eisel
commented
“Open this file…” (like the other descriptions). “Open **this** file…” (like the other descriptions).
|
||||
{FILE_EXTERNAL_OPERATION_FOLDER_OPEN,
|
||||
Julian Eisel
commented
How about The existing descriptions here aren't great either, noted that in #104531. How about `"Display this file in a new Finder window"`?
The existing descriptions here aren't great either, noted that in #104531.
|
||||
"FOLDER_OPEN",
|
||||
ICON_NONE,
|
||||
#ifdef __APPLE__
|
||||
"Reveal in Finder",
|
||||
"Reveal this folder in a new Finder window"
|
||||
#else
|
||||
"Open in File Explorer",
|
||||
"Open this folder in a system file browser"
|
||||
ankitm marked this conversation as resolved
Outdated
Julian Eisel
commented
“in a system file browser“ “in **a** system file browser“
|
||||
#endif
|
||||
},
|
||||
{FILE_EXTERNAL_OPERATION_FILE_REVEAL,
|
||||
Julian Eisel
commented
`"Display this directory in a new Finder window"`? (Is it the selected directory or the current directory?)
Julian Eisel
commented
“…this file…” “…this file…”
|
||||
"FILE_REVEAL",
|
||||
ICON_NONE,
|
||||
#ifdef __APPLE__
|
||||
"Reveal in Finder",
|
||||
"Reveal this file in a new Finder window"
|
||||
#else
|
||||
"Open in File Explorer",
|
||||
"Open this file in a system file browser"
|
||||
#endif
|
||||
},
|
||||
{FILE_EXTERNAL_OPERATION_EDIT, "EDIT", ICON_NONE, "Edit", "Edit this file"},
|
||||
{FILE_EXTERNAL_OPERATION_NEW, "NEW", ICON_NONE, "New", "Create a new file of this type"},
|
||||
{FILE_EXTERNAL_OPERATION_FIND,
|
||||
"FIND",
|
||||
ICON_NONE,
|
||||
"Find File",
|
||||
"Search for files of this type"},
|
||||
{FILE_EXTERNAL_OPERATION_SHOW, "SHOW", ICON_NONE, "Show", "Show this file"},
|
||||
{FILE_EXTERNAL_OPERATION_PLAY, "PLAY", ICON_NONE, "Play", "Play this file"},
|
||||
{FILE_EXTERNAL_OPERATION_BROWSE, "BROWSE", ICON_NONE, "Browse", "Browse this file"},
|
||||
{FILE_EXTERNAL_OPERATION_PREVIEW, "PREVIEW", ICON_NONE, "Preview", "Preview this file"},
|
||||
{FILE_EXTERNAL_OPERATION_PRINT, "PRINT", ICON_NONE, "Print", "Print this file"},
|
||||
{FILE_EXTERNAL_OPERATION_INSTALL, "INSTALL", ICON_NONE, "Install", "Install this file"},
|
||||
{FILE_EXTERNAL_OPERATION_RUNAS, "RUNAS", ICON_NONE, "Run As User", "Run as specific user"},
|
||||
{FILE_EXTERNAL_OPERATION_PROPERTIES,
|
||||
Julian Eisel
commented
Use "file" instead of "item". Use "file" instead of "item".
|
||||
"PROPERTIES",
|
||||
0,
|
||||
ICON_NONE,
|
||||
#ifdef __APPLE__
|
||||
"Get Info",
|
||||
"Open the Get Info window for this file"
|
||||
#else
|
||||
"Properties",
|
||||
"Show OS Properties for this item"},
|
||||
"Show OS Properties for this file"
|
||||
#endif
|
||||
},
|
||||
{FILE_EXTERNAL_OPERATION_FOLDER_FIND,
|
||||
"FOLDER_FIND",
|
||||
0,
|
||||
ICON_NONE,
|
||||
"Find in Folder",
|
||||
"Search for items in this folder"},
|
||||
{FILE_EXTERNAL_OPERATION_FOLDER_CMD,
|
||||
"Search for files in this folder"},
|
||||
{FILE_EXTERNAL_OPERATION_FOLDER_TERMINAL,
|
||||
"CMD",
|
||||
0,
|
||||
ICON_NONE,
|
||||
#ifdef __APPLE__
|
||||
"Open in Terminal",
|
||||
"Open a terminal window with this folder/ file's parent folder as the current working "
|
||||
"directory"
|
||||
#else
|
||||
"Command Prompt Here",
|
||||
"Open a command prompt here"},
|
||||
"Open a command prompt here"
|
||||
#endif
|
||||
},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
static int file_external_operation_exec(bContext *C, wmOperator *op)
|
||||
|
@ -1812,7 +1854,7 @@ static int file_external_operation_exec(bContext *C, wmOperator *op)
|
|||
|
||||
WM_cursor_set(CTX_wm_window(C), WM_CURSOR_WAIT);
|
||||
|
||||
#ifdef WIN32
|
||||
#if defined(WIN32) || defined(__APPLE__)
|
||||
const FileExternalOperation operation = (FileExternalOperation)RNA_enum_get(op->ptr,
|
||||
"operation");
|
||||
if (BLI_file_external_operation_execute(filepath, operation)) {
|
||||
|
@ -1871,7 +1913,7 @@ void FILE_OT_external_operation(wmOperatorType *ot)
|
|||
file_external_operation,
|
||||
FILE_EXTERNAL_OPERATION_OPEN,
|
||||
"Operation",
|
||||
"Operation to perform on the file or path");
|
||||
"Operation to perform on the file or folder");
|
||||
}
|
||||
|
||||
static void file_os_operations_menu_item(uiLayout *layout,
|
||||
|
@ -1879,7 +1921,7 @@ static void file_os_operations_menu_item(uiLayout *layout,
|
|||
const char *path,
|
||||
FileExternalOperation operation)
|
||||
{
|
||||
#ifdef WIN32
|
||||
#if defined(WIN32) || defined(__APPLE__)
|
||||
if (!BLI_file_external_operation_supported(path, operation)) {
|
||||
return;
|
||||
}
|
||||
|
@ -1945,7 +1987,7 @@ static void file_os_operations_menu_draw(const bContext *C_const, Menu *menu)
|
|||
|
||||
if (fileentry->typeflag & FILE_TYPE_DIR) {
|
||||
file_os_operations_menu_item(layout, ot, path, FILE_EXTERNAL_OPERATION_FOLDER_OPEN);
|
||||
file_os_operations_menu_item(layout, ot, path, FILE_EXTERNAL_OPERATION_FOLDER_CMD);
|
||||
file_os_operations_menu_item(layout, ot, path, FILE_EXTERNAL_OPERATION_FOLDER_TERMINAL);
|
||||
file_os_operations_menu_item(layout, ot, path, FILE_EXTERNAL_OPERATION_PROPERTIES);
|
||||
}
|
||||
else {
|
||||
|
@ -1961,7 +2003,8 @@ static void file_os_operations_menu_draw(const bContext *C_const, Menu *menu)
|
|||
file_os_operations_menu_item(layout, ot, path, FILE_EXTERNAL_OPERATION_INSTALL);
|
||||
file_os_operations_menu_item(layout, ot, path, FILE_EXTERNAL_OPERATION_RUNAS);
|
||||
file_os_operations_menu_item(layout, ot, root, FILE_EXTERNAL_OPERATION_FOLDER_OPEN);
|
||||
file_os_operations_menu_item(layout, ot, root, FILE_EXTERNAL_OPERATION_FOLDER_CMD);
|
||||
file_os_operations_menu_item(layout, ot, path, FILE_EXTERNAL_OPERATION_FILE_REVEAL);
|
||||
file_os_operations_menu_item(layout, ot, root, FILE_EXTERNAL_OPERATION_FOLDER_TERMINAL);
|
||||
file_os_operations_menu_item(layout, ot, path, FILE_EXTERNAL_OPERATION_PROPERTIES);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
“Not all operations may be supported on all platforms.”