diff --git a/MacPass/Base.lproj/EntryInspectorView.xib b/MacPass/Base.lproj/EntryInspectorView.xib index f17c4791..59b96673 100644 --- a/MacPass/Base.lproj/EntryInspectorView.xib +++ b/MacPass/Base.lproj/EntryInspectorView.xib @@ -72,7 +72,7 @@ - + @@ -96,9 +96,9 @@ - + - + @@ -172,7 +172,7 @@ - + @@ -217,11 +217,11 @@ - + - + @@ -342,7 +342,7 @@ - + @@ -371,7 +371,7 @@ - + @@ -398,7 +398,7 @@ - + @@ -456,7 +456,7 @@ - + @@ -643,15 +643,15 @@ - + - + - + @@ -664,7 +664,7 @@ - + @@ -721,7 +721,7 @@ - + @@ -734,7 +734,7 @@ - + @@ -844,7 +844,7 @@ - + @@ -854,7 +854,7 @@ - + @@ -889,12 +889,13 @@ + - + - + diff --git a/MacPass/MPAttachmentTableDataSource.m b/MacPass/MPAttachmentTableDataSource.m index 14f55821..8f1edc52 100644 --- a/MacPass/MPAttachmentTableDataSource.m +++ b/MacPass/MPAttachmentTableDataSource.m @@ -21,11 +21,13 @@ // #import "MPAttachmentTableDataSource.h" +#import "KPKBinary+MPAdditions.h" #import "MPDocument.h" @implementation MPAttachmentTableDataSource - (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id)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 *)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 *fileNames = [[NSMutableArray alloc] init]; + NSArray *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)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 diff --git a/MacPass/MPEntryInspectorViewController.m b/MacPass/MPEntryInspectorViewController.m index bba14163..1c04a1b9 100644 --- a/MacPass/MPEntryInspectorViewController.m +++ b/MacPass/MPEntryInspectorViewController.m @@ -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; diff --git a/MacPass/MPEntryTableDataSource.m b/MacPass/MPEntryTableDataSource.m index 27e8466e..c9a93690 100644 --- a/MacPass/MPEntryTableDataSource.m +++ b/MacPass/MPEntryTableDataSource.m @@ -34,6 +34,10 @@ // FIXME: change drag image to use only the first column regardless of drag start - (id)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)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)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 *)_readEntryUUIDsFromPasterboard:(NSPasteboard *)pasteboard { + if([pasteboard.types containsObject:KPKEntryUUDIUTI]) { + if([pasteboard canReadObjectForClasses:@[NSUUID.class] options:nil]) { + return [pasteboard readObjectsForClasses:@[NSUUID.class] options:nil]; + } + } + return @[]; +} + +- (NSArray *)_readEntriesFromPasteboard:(NSPasteboard *)pasteboard { + return @[]; } @end