Attachments now can be dragged out onto the finder.

This commit is contained in:
Michael Starke
2018-11-09 15:31:45 +01:00
parent cac974a30e
commit b58211c4da
4 changed files with 137 additions and 49 deletions

View File

@@ -72,7 +72,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="124">
<rect key="frame" x="241" y="428" width="32" height="25"/>
<rect key="frame" x="241" y="428" width="32" height="23"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="176"/>
</constraints>
@@ -96,9 +96,9 @@
<rect key="frame" x="20" y="26" width="253" height="396"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="F3N-QI-Di5">
<rect key="frame" x="1" y="1" width="251" height="394"/>
<autoresizingMask key="autoresizingMask"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="36" rowSizeStyle="automatic" viewBased="YES" id="137">
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowHeight="36" rowSizeStyle="automatic" viewBased="YES" id="137">
<rect key="frame" x="0.0" y="0.0" width="251" height="394"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
@@ -172,7 +172,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="42g-QS-XtW">
<rect key="frame" x="205" y="5" width="40" height="25"/>
<rect key="frame" x="205" y="6" width="40" height="23"/>
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" pullsDown="YES" altersStateOfSelectedItem="NO" id="nJc-UT-cas">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
@@ -217,11 +217,11 @@
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="100" id="KFw-Ma-DSd"/>
</constraints>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="138">
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="138">
<rect key="frame" x="1" y="147" width="52" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="139">
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="139">
<rect key="frame" x="37" y="1" width="16" height="2"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
@@ -342,7 +342,7 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="59">
<rect key="frame" x="20" y="519" width="251" height="25"/>
<rect key="frame" x="20" y="520" width="251" height="23"/>
<buttonCell key="cell" type="roundTextured" title="Generate" bezelStyle="texturedRounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="64">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -371,7 +371,7 @@
</connections>
</secureTextField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="61">
<rect key="frame" x="239" y="549" width="32" height="25"/>
<rect key="frame" x="239" y="550" width="32" height="23"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="rSr-fw-11t"/>
</constraints>
@@ -398,7 +398,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8">
<rect key="frame" x="239" y="437" width="32" height="25"/>
<rect key="frame" x="239" y="438" width="32" height="23"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="Ped-nx-uti"/>
</constraints>
@@ -456,7 +456,7 @@
</connections>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="QSX-Xo-tcH">
<rect key="frame" x="20" y="18" width="251" height="25"/>
<rect key="frame" x="20" y="19" width="251" height="23"/>
<buttonCell key="cell" type="roundTextured" title="Show Plugin Data" bezelStyle="texturedRounded" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="X9y-K7-lix">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -643,15 +643,15 @@
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="160" id="B8z-Sq-4j9"/>
</constraints>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="Y4S-4R-dwJ">
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Y4S-4R-dwJ">
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="abK-py-K7A">
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="abK-py-K7A">
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Iy9-9L-Aev">
<rect key="frame" x="249" y="106" width="32" height="25"/>
<rect key="frame" x="249" y="107" width="32" height="23"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="mjs-Yf-Cb2"/>
</constraints>
@@ -664,7 +664,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="AAj-Ak-z46">
<rect key="frame" x="209" y="106" width="32" height="25"/>
<rect key="frame" x="209" y="107" width="32" height="23"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="vWY-ez-gy9"/>
</constraints>
@@ -721,7 +721,7 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="m1C-m8-BKR">
<rect key="frame" x="249" y="8" width="32" height="25"/>
<rect key="frame" x="249" y="9" width="32" height="23"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="Ek5-IH-qGo"/>
</constraints>
@@ -734,7 +734,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="HDS-Bz-jrr">
<rect key="frame" x="249" y="325" width="32" height="25"/>
<rect key="frame" x="249" y="326" width="32" height="23"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="CbR-2P-dZH"/>
</constraints>
@@ -844,7 +844,7 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="198">
<rect key="frame" x="190" y="8" width="32" height="25"/>
<rect key="frame" x="190" y="9" width="32" height="23"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="32" id="215"/>
</constraints>
@@ -854,7 +854,7 @@
</buttonCell>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="tDI-EL-JGB">
<rect key="frame" x="150" y="8" width="32" height="25"/>
<rect key="frame" x="150" y="9" width="32" height="23"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="32" id="JGO-sl-fdM"/>
</constraints>
@@ -889,12 +889,13 @@
</tableColumns>
</tableView>
</subviews>
<nil key="backgroundColor"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="192">
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="192">
<rect key="frame" x="1" y="130" width="259" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="191">
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="191">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>

View File

@@ -21,11 +21,13 @@
//
#import "MPAttachmentTableDataSource.h"
#import "KPKBinary+MPAdditions.h"
#import "MPDocument.h"
@implementation MPAttachmentTableDataSource
- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id<NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation {
/* allow drag between databases? */
NSPasteboard *draggingPasteBoard = [info draggingPasteboard];
NSArray *arrayOfURLs = [draggingPasteBoard readObjectsForClasses:@[NSURL.class] options:nil];
NSUInteger numberOfDirectories = 0;
@@ -33,7 +35,7 @@
if(url.fileURL || url.fileReferenceURL) {
NSError *error = nil;
NSDictionary *resourceKeys = [url resourceValuesForKeys:@[NSURLIsDirectoryKey] error:&error];
if( [resourceKeys[ NSURLIsDirectoryKey ] boolValue] == YES ) {
if([resourceKeys[NSURLIsDirectoryKey] boolValue] == YES) {
numberOfDirectories++;
}
continue;
@@ -43,6 +45,10 @@
if(numberOfDirectories == arrayOfURLs.count) {
return NSDragOperationNone;
}
if(dropOperation == NSTableViewDropOn) {
[tableView setDropRow:row+1 dropOperation:NSTableViewDropAbove];
}
return NSDragOperationCopy;
}
@@ -50,40 +56,67 @@
MPDocument *document = tableView.window.windowController.document;
KPKEntry *entry = document.selectedEntries.count == 1 ? document.selectedEntries.lastObject : nil;
NSPasteboard *draggingPasteBoard = [info draggingPasteboard];
NSArray *arrayOfURLs = [draggingPasteBoard readObjectsForClasses:@[NSURL.class] options:nil];
NSArray *arrayOfURLs = [info.draggingPasteboard readObjectsForClasses:@[NSURL.class] options:nil];
for(NSURL *fileUrl in arrayOfURLs) {
[document addAttachment:fileUrl toEntry:entry];
}
return YES;
}
- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard {
return NO;
/*
NSString *extension;
if([rowIndexes count] != 1) {
return NO; // We only work with one file at a time
[pboard declareTypes:@[NSFilesPromisePboardType] owner:nil];
MPDocument *document = tableView.window.windowController.document;
KPKEntry *entry = document.selectedEntries.count == 1 ? document.selectedEntries.lastObject : nil;
NSMutableArray *fileNames = [[NSMutableArray alloc] init];
for(KPKBinary *binary in [entry.binaries objectsAtIndexes:rowIndexes]) {
if(binary.name) {
[fileNames addObject:binary.name];
}
}
MPDocument *document = [[[tableView window] windowController] document];
id entry = document.selectedEntry;
NSUInteger row = [rowIndexes lastIndex];
if([entry isKindOfClass:[Kdb3Entry class]]) {
Kdb3Entry *entryV3 = (Kdb3Entry *)entry;
extension = [entryV3.binaryDesc pathExtension];
}
else if([entry isKindOfClass:[Kdb4Entry class]]) {
Kdb4Entry *entryV4 = (Kdb4Entry *)entry;
BinaryRef *binaryRef = entryV4.binaries[row];
extension = [binaryRef.key pathExtension];
}
NSString *uti = CFBridgingRelease(UTTypeCreatePreferredIdentifierForTag( kUTTagClassFilenameExtension, (__bridge CFStringRef)(extension), NULL ));
[pboard setPropertyList:@[uti] forType:(NSString *)kPasteboardTypeFilePromiseContent];
[pboard setPropertyList:@[uti] forType:(NSString *)kPasteboardTypeFileURLPromise ];
return YES;*/
[pboard setPropertyList:fileNames forType:NSFilesPromisePboardType];
return YES;
}
- (NSArray<NSString *> *)tableView:(NSTableView *)tableView namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination forDraggedRowsWithIndexes:(NSIndexSet *)indexSet {
MPDocument *document = tableView.window.windowController.document;
KPKEntry *entry = document.selectedEntries.count == 1 ? document.selectedEntries.lastObject : nil;
NSMutableArray<NSString *> *fileNames = [[NSMutableArray alloc] init];
NSArray<KPKBinary *> *draggedBinaries = [entry.binaries objectsAtIndexes:indexSet];
for(KPKBinary *binary in draggedBinaries) {
if(binary.name) {
[fileNames addObject:binary.name];
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0);
dispatch_async(queue, ^{
NSError *error;
NSURL *saveLocation = [dropDestination URLByAppendingPathComponent:binary.name];
BOOL success = [binary saveToLocation:saveLocation error:&error];
if(!success && error) {
dispatch_async(dispatch_get_main_queue(), ^{
[NSApp presentError:error];
});
}
});
}
}
return [fileNames copy];
}
/*
- (id<NSPasteboardWriting>)tableView:(NSTableView *)tableView pasteboardWriterForRow:(NSInteger)row {
MPDocument *document = tableView.window.windowController.document;
KPKEntry *entry = document.selectedEntries.count == 1 ? document.selectedEntries.lastObject : nil;
if(!entry) {
return nil;
}
if (@available(macOS 10.12, *)) {
KPKBinary *binary = entry.binaries[row];
NSFilePromiseProvider *provider = [[NSFilePromiseProvider alloc] initWithFileType:(NSString *)kUTTypeXML delegate:binary];
return provider;
}
return nil;
}
*/
@end

View File

@@ -131,6 +131,7 @@ typedef NS_ENUM(NSUInteger, MPEntryTab) {
self.attachmentTableView.delegate = _attachmentTableDelegate;
self.attachmentTableView.dataSource = _attachmentDataSource;
[self.attachmentTableView registerForDraggedTypes:@[NSFilenamesPboardType]];
[self.attachmentTableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
/* extract custom field table view */
NSView *customFieldTableView = self.customFieldsTableView;

View File

@@ -34,6 +34,10 @@
// FIXME: change drag image to use only the first column regardless of drag start
- (id<NSPasteboardWriting>)tableView:(NSTableView *)tableView pasteboardWriterForRow:(NSInteger)row {
if(MPDisplayModeEntries != self.viewController.displayMode) {
return nil;
}
id item = self.viewController.entryArrayController.arrangedObjects[row];
if([item isKindOfClass:KPKEntry.class]) {
return item;
@@ -41,7 +45,16 @@
return nil;
}
- (void)tableView:(NSTableView *)tableView draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint forRowIndexes:(nonnull NSIndexSet *)rowIndexes {
session.draggingFormation = NSDraggingFormationList;
}
- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id<NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation {
/* we do not accept drops if we are in history or search display mode */
if(MPDisplayModeEntries != self.viewController.displayMode) {
return NSDragOperationNone;
}
BOOL makeCopy = (info.draggingSourceOperationMask == NSDragOperationCopy);
if(dropOperation == NSTableViewDropOn) {
[tableView setDropRow:row+1 dropOperation:NSTableViewDropAbove];
@@ -49,8 +62,48 @@
return makeCopy ? NSDragOperationCopy : NSDragOperationMove;
}
- (void)tableView:(NSTableView *)tableView draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint forRowIndexes:(nonnull NSIndexSet *)rowIndexes {
session.draggingFormation = NSDraggingFormationList;
- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id<NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation {
/* local drag */
BOOL copyItems = info.draggingSourceOperationMask == NSDragOperationCopy;
MPDocument *document = tableView.window.windowController.document;
if(document.currentTargetGroups.count != 1) {
return NO;
}
KPKGroup *targetGroup = document.currentTargetGroups.firstObject;
if(info.draggingSource == tableView) {
if(copyItems) {
for(NSUUID *entryUUID in [self _readEntryUUIDsFromPasterboard:info.draggingPasteboard].reverseObjectEnumerator) {
KPKEntry *entry = [[document findEntry:entryUUID] copyWithTitle:nil options:kKPKCopyOptionNone];
[entry addToGroup:targetGroup atIndex:row];
[entry.undoManager setActionName:NSLocalizedString(@"COPY_ENTRY", @"Action name when an entry was moved")];
}
}
else {
for(NSUUID *entryUUID in [self _readEntryUUIDsFromPasterboard:info.draggingPasteboard].reverseObjectEnumerator) {
KPKEntry *entry = [document findEntry:entryUUID];
[entry moveToGroup:entry.parent atIndex:row];
[entry.undoManager setActionName:NSLocalizedString(@"MOVE_ENTRY", @"Action name when an entry was moved")];
}
}
return YES;
}
else {
// external drop
}
return NO;
}
- (NSArray<NSUUID *> *)_readEntryUUIDsFromPasterboard:(NSPasteboard *)pasteboard {
if([pasteboard.types containsObject:KPKEntryUUDIUTI]) {
if([pasteboard canReadObjectForClasses:@[NSUUID.class] options:nil]) {
return [pasteboard readObjectsForClasses:@[NSUUID.class] options:nil];
}
}
return @[];
}
- (NSArray<KPKEntry *> *)_readEntriesFromPasteboard:(NSPasteboard *)pasteboard {
return @[];
}
@end