如何将 NSPasteboard 与 kPasteboardTypeFileURLPromise 一起用于 copy/paste?
How to use NSPasteboard with kPasteboardTypeFileURLPromise for copy/paste?
我的应用程序想要向远程存储的文件的粘贴板添加一个承诺,并且可能永远不会粘贴 — 类似于粘贴从控制 VM 或其他远程系统的会话复制的文件。理想情况下,用户可以粘贴到 Finder 文件夹(或桌面)中,然后 promise 将触发并离开。我愿意处理一旦触发履行承诺的问题,但我一直无法获得触发的承诺。
我找到的所有 promise 代码都处理拖放,这不是我需要的功能(尽管可能需要来自 DnD 的某些东西才能使 promise 工作?)
我试过将 NSFilePromiseProvider
与委托一起使用,并将其添加到粘贴板。我可以使用剪贴板查看器查看粘贴板上的条目,但是当我在 Finder 中粘贴时,没有任何反应,也没有调用委托方法。我可以通过让剪贴板查看器访问条目来触发委托方法,所以我知道很多都已连接。
@interface ClipboardMacPromise : NSFilePromiseProvider<NSFilePromiseProviderDelegate>
{
NSString* m_file;
}
@end
@implementation ClipboardMacPromise
- (id)initWithFileType:(NSString*)type andFile:(NSString*)file
{
m_file = file;
return [super initWithFileType:type delegate:self];
}
- (NSString *)filePromiseProvider:(NSFilePromiseProvider*)filePromiseProvider fileNameForType:(NSString *)fileType
{
return m_file;
}
- (void)filePromiseProvider:(NSFilePromiseProvider*)filePromiseProvider writePromiseToURL:(NSURL *)url completionHandler:(void (^)(NSError * _Nullable errorOrNil))completionHandler
{
// Finder can't paste, so we never get here...
}
@end
NSPasteboard* pboard = [NSPasteboard generalPasteboard];
[pboard clearContents];
NSMutableArray* items = [[NSMutableArray alloc] init];
ClipboardMacPromise* promise = [[ClipboardMacPromise alloc] initWithFileType:(NSString*)kUTTypeFileURL andFile:@"dummy.txt"];
[items addObject:promise];
[pboard writeObjects:items];
我也尝试过 NSPasteboardItem
和 NSPasteboardItemDataProvider
,我在 kUTITypeFileURL
上设置了内容承诺。它在粘贴板上提供了非常相似的条目,但是当我粘贴到 finder 中时仍然没有任何动作。剪贴板查看器在访问单个粘贴板条目时将再次触发提供程序罚款。 (NSPasteboard
的 declareTypes:owner:
具有相同的行为)
@interface ClipboardMacPromise : NSPasteboardItem<NSPasteboardItemDataProvider>
{
NSString* m_file;
}
@end
@implementation ClipboardMacPromise
- (id)initWithFile:(NSString*)file
{
m_file = file;
id _self = [super init];
if (_self) {
[_self setDataProvider:_self forTypes:@[(NSString*)kPasteboardTypeFileURLPromise]];
[_self setString:(NSString*)kUTTypeFileURL forType:(NSString*)kPasteboardTypeFilePromiseContent];
}
return _self;
}
- (void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type
{
// we don't get here when we paste in Finder because
// Finder doesn't think there's anything to paste
// but using a clipboard viewer, we can force the promise to
// resolve and we do get here
}
@end
NSPasteboard* pboard = [NSPasteboard generalPasteboard];
[pboard clearContents];
NSMutableArray* items = [[NSMutableArray alloc] init];
ClipboardMacPromise* promise = [[ClipboardMacPromise alloc] initWithFile:@"file:///tmp/dummy.txt"];
[items addObject:promise];
[pboard writeObjects:items];
为了完整起见,这是我的 Carbon 尝试,因为 Pasteboard.h 似乎详细说明了它在 copy/paste 场景中应该如何工作......但它仍然没有为 Finder 提供它正在寻找的东西.生成的剪贴板条目在三个实现之间看起来非常相似。
OSStatus PasteboardPromiseKeeperProc(PasteboardRef pasteboard, PasteboardItemID item, CFStringRef flavorType, void * _Nullable context)
{
// 6) The sender's promise callback for kPasteboardTypeFileURLPromise is called.
string s = "dummy.txt";
CFDataRef inData = CFDataCreate(kCFAllocatorDefault, (UInt8*)s.c_str(), s.size());
PasteboardPutItemFlavor(pasteboard, item, flavorType, inData, 0);
return noErr;
}
PasteboardRef p = NULL;
PasteboardCreate(kPasteboardClipboard, &p);
PasteboardClear(p);
PasteboardSetPromiseKeeper(p, &PasteboardPromiseKeeperProc, this);
// 1) The sender promises kPasteboardTypeFileURLPromise for a file yet to be created.
PasteboardPutItemFlavor(p, (PasteboardItemID)1, kPasteboardTypeFileURLPromise, kPasteboardPromisedData, 0);
// 2) The sender adds kPasteboardTypeFilePromiseContent containing the UTI describing the file's content.
PasteboardPutItemFlavor(p, (PasteboardItemID)2, kPasteboardTypeFilePromiseContent,CFStringCreateExternalRepresentation(NULL, kUTTypeFileURL, kCFStringEncodingUTF8, 0), 0);
粘贴板上好像真的有Finder要找的某个UTI,我没有。如果我将 kUTTypeFileURL
直接放在剪贴板上,看起来 finder 在提供粘贴之前实际上会检查文件是否存在(即触发 Catalina 的桌面访问提示)。
有谁知道是否可以或如何通过 Copy/Paste 而不是拖放来向 Finder 提供文件承诺?
这里的关键似乎是 Finder 要求文件实际存在于磁盘上才能为文件启用粘贴操作 URL。这个细节排除了 promises 为 copy/paste 工作的可能性——至少在 Finder 中是这样。
因此,正确的解决方案需要一个虚拟化文件系统(如 FUSE),以便可以在文件系统级别做出并实现承诺。因此,可以将临时零长度文件的集合写入磁盘,并将实际文件 URL 添加到粘贴板。这满足了 Finder 必须启用粘贴的要求。然后,当进行粘贴操作时,文件数据将从虚拟化文件系统中读取,而虚拟化文件系统又可以从远程系统中检索实际数据。 Finder none 更聪明。副本甚至会有一个内置的进度条!
Microsoft 的 Mac RDP 客户端似乎主要以这种方式工作,尽管我只能让它复制零长度文件,因此这可能比听起来更难正确。
我的应用程序想要向远程存储的文件的粘贴板添加一个承诺,并且可能永远不会粘贴 — 类似于粘贴从控制 VM 或其他远程系统的会话复制的文件。理想情况下,用户可以粘贴到 Finder 文件夹(或桌面)中,然后 promise 将触发并离开。我愿意处理一旦触发履行承诺的问题,但我一直无法获得触发的承诺。
我找到的所有 promise 代码都处理拖放,这不是我需要的功能(尽管可能需要来自 DnD 的某些东西才能使 promise 工作?)
我试过将 NSFilePromiseProvider
与委托一起使用,并将其添加到粘贴板。我可以使用剪贴板查看器查看粘贴板上的条目,但是当我在 Finder 中粘贴时,没有任何反应,也没有调用委托方法。我可以通过让剪贴板查看器访问条目来触发委托方法,所以我知道很多都已连接。
@interface ClipboardMacPromise : NSFilePromiseProvider<NSFilePromiseProviderDelegate>
{
NSString* m_file;
}
@end
@implementation ClipboardMacPromise
- (id)initWithFileType:(NSString*)type andFile:(NSString*)file
{
m_file = file;
return [super initWithFileType:type delegate:self];
}
- (NSString *)filePromiseProvider:(NSFilePromiseProvider*)filePromiseProvider fileNameForType:(NSString *)fileType
{
return m_file;
}
- (void)filePromiseProvider:(NSFilePromiseProvider*)filePromiseProvider writePromiseToURL:(NSURL *)url completionHandler:(void (^)(NSError * _Nullable errorOrNil))completionHandler
{
// Finder can't paste, so we never get here...
}
@end
NSPasteboard* pboard = [NSPasteboard generalPasteboard];
[pboard clearContents];
NSMutableArray* items = [[NSMutableArray alloc] init];
ClipboardMacPromise* promise = [[ClipboardMacPromise alloc] initWithFileType:(NSString*)kUTTypeFileURL andFile:@"dummy.txt"];
[items addObject:promise];
[pboard writeObjects:items];
我也尝试过 NSPasteboardItem
和 NSPasteboardItemDataProvider
,我在 kUTITypeFileURL
上设置了内容承诺。它在粘贴板上提供了非常相似的条目,但是当我粘贴到 finder 中时仍然没有任何动作。剪贴板查看器在访问单个粘贴板条目时将再次触发提供程序罚款。 (NSPasteboard
的 declareTypes:owner:
具有相同的行为)
@interface ClipboardMacPromise : NSPasteboardItem<NSPasteboardItemDataProvider>
{
NSString* m_file;
}
@end
@implementation ClipboardMacPromise
- (id)initWithFile:(NSString*)file
{
m_file = file;
id _self = [super init];
if (_self) {
[_self setDataProvider:_self forTypes:@[(NSString*)kPasteboardTypeFileURLPromise]];
[_self setString:(NSString*)kUTTypeFileURL forType:(NSString*)kPasteboardTypeFilePromiseContent];
}
return _self;
}
- (void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type
{
// we don't get here when we paste in Finder because
// Finder doesn't think there's anything to paste
// but using a clipboard viewer, we can force the promise to
// resolve and we do get here
}
@end
NSPasteboard* pboard = [NSPasteboard generalPasteboard];
[pboard clearContents];
NSMutableArray* items = [[NSMutableArray alloc] init];
ClipboardMacPromise* promise = [[ClipboardMacPromise alloc] initWithFile:@"file:///tmp/dummy.txt"];
[items addObject:promise];
[pboard writeObjects:items];
为了完整起见,这是我的 Carbon 尝试,因为 Pasteboard.h 似乎详细说明了它在 copy/paste 场景中应该如何工作......但它仍然没有为 Finder 提供它正在寻找的东西.生成的剪贴板条目在三个实现之间看起来非常相似。
OSStatus PasteboardPromiseKeeperProc(PasteboardRef pasteboard, PasteboardItemID item, CFStringRef flavorType, void * _Nullable context)
{
// 6) The sender's promise callback for kPasteboardTypeFileURLPromise is called.
string s = "dummy.txt";
CFDataRef inData = CFDataCreate(kCFAllocatorDefault, (UInt8*)s.c_str(), s.size());
PasteboardPutItemFlavor(pasteboard, item, flavorType, inData, 0);
return noErr;
}
PasteboardRef p = NULL;
PasteboardCreate(kPasteboardClipboard, &p);
PasteboardClear(p);
PasteboardSetPromiseKeeper(p, &PasteboardPromiseKeeperProc, this);
// 1) The sender promises kPasteboardTypeFileURLPromise for a file yet to be created.
PasteboardPutItemFlavor(p, (PasteboardItemID)1, kPasteboardTypeFileURLPromise, kPasteboardPromisedData, 0);
// 2) The sender adds kPasteboardTypeFilePromiseContent containing the UTI describing the file's content.
PasteboardPutItemFlavor(p, (PasteboardItemID)2, kPasteboardTypeFilePromiseContent,CFStringCreateExternalRepresentation(NULL, kUTTypeFileURL, kCFStringEncodingUTF8, 0), 0);
粘贴板上好像真的有Finder要找的某个UTI,我没有。如果我将 kUTTypeFileURL
直接放在剪贴板上,看起来 finder 在提供粘贴之前实际上会检查文件是否存在(即触发 Catalina 的桌面访问提示)。
有谁知道是否可以或如何通过 Copy/Paste 而不是拖放来向 Finder 提供文件承诺?
这里的关键似乎是 Finder 要求文件实际存在于磁盘上才能为文件启用粘贴操作 URL。这个细节排除了 promises 为 copy/paste 工作的可能性——至少在 Finder 中是这样。
因此,正确的解决方案需要一个虚拟化文件系统(如 FUSE),以便可以在文件系统级别做出并实现承诺。因此,可以将临时零长度文件的集合写入磁盘,并将实际文件 URL 添加到粘贴板。这满足了 Finder 必须启用粘贴的要求。然后,当进行粘贴操作时,文件数据将从虚拟化文件系统中读取,而虚拟化文件系统又可以从远程系统中检索实际数据。 Finder none 更聪明。副本甚至会有一个内置的进度条!
Microsoft 的 Mac RDP 客户端似乎主要以这种方式工作,尽管我只能让它复制零长度文件,因此这可能比听起来更难正确。