mirror of
https://github.com/MacPass/MacPass.git
synced 2025-12-13 15:52:19 +00:00
Added support for dragging multiple entries as well as groups. Drag and drop between entry lists is still missing.
This commit is contained in:
2
Cartfile
2
Cartfile
@@ -1,3 +1,3 @@
|
|||||||
github "sparkle-project/Sparkle" ~> 1.18.1
|
github "sparkle-project/Sparkle" ~> 1.18.1
|
||||||
github "MacPass/KeePassKit" ~> 2.2.1
|
github "MacPass/KeePassKit" ~> 2.3
|
||||||
github "mstarke/HNHUi" ~> 3.0
|
github "mstarke/HNHUi" ~> 3.0
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
github "MacPass/KeePassKit" "2.2.1"
|
github "MacPass/KeePassKit" "2.3.1"
|
||||||
github "mstarke/HNHUi" "3.0"
|
github "mstarke/HNHUi" "3.0"
|
||||||
github "robbiehanson/KissXML" "5.2.3"
|
github "robbiehanson/KissXML" "5.2.3"
|
||||||
github "sparkle-project/Sparkle" "1.20.0"
|
github "sparkle-project/Sparkle" "1.20.0"
|
||||||
|
|||||||
@@ -130,6 +130,8 @@
|
|||||||
4C586F9E16D07ABD00E7DB57 /* 00_PasswordTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4C586F9D16D07ABD00E7DB57 /* 00_PasswordTemplate.pdf */; };
|
4C586F9E16D07ABD00E7DB57 /* 00_PasswordTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4C586F9D16D07ABD00E7DB57 /* 00_PasswordTemplate.pdf */; };
|
||||||
4C586FA016D07D7200E7DB57 /* 01_PackageNetworkTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4C586F9F16D07D7200E7DB57 /* 01_PackageNetworkTemplate.pdf */; };
|
4C586FA016D07D7200E7DB57 /* 01_PackageNetworkTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4C586F9F16D07D7200E7DB57 /* 01_PackageNetworkTemplate.pdf */; };
|
||||||
4C586FA216D07F6A00E7DB57 /* 02_MessageBoxWarningTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4C586FA116D07F6A00E7DB57 /* 02_MessageBoxWarningTemplate.pdf */; };
|
4C586FA216D07F6A00E7DB57 /* 02_MessageBoxWarningTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4C586FA116D07F6A00E7DB57 /* 02_MessageBoxWarningTemplate.pdf */; };
|
||||||
|
4C58A4A32192EC1600B13370 /* NSIndexPath+MPAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C58A4A22192EC1600B13370 /* NSIndexPath+MPAdditions.m */; };
|
||||||
|
4C58A4A82192EEBE00B13370 /* MPTestIndexPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C58A4A72192EEBE00B13370 /* MPTestIndexPathAdditions.m */; };
|
||||||
4C5A11FE1708DE8700223D8A /* MPPasswordCreatorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C5A11FC1708DE8700223D8A /* MPPasswordCreatorViewController.m */; };
|
4C5A11FE1708DE8700223D8A /* MPPasswordCreatorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C5A11FC1708DE8700223D8A /* MPPasswordCreatorViewController.m */; };
|
||||||
4C5EF816218CA03F0003C00E /* MPAutotypeParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C5EF815218CA03F0003C00E /* MPAutotypeParser.m */; };
|
4C5EF816218CA03F0003C00E /* MPAutotypeParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C5EF815218CA03F0003C00E /* MPAutotypeParser.m */; };
|
||||||
4C5FE9AE17843CE20001D5A8 /* MPSelectedAttachmentTableCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C5FE9AD17843CE20001D5A8 /* MPSelectedAttachmentTableCellView.m */; };
|
4C5FE9AE17843CE20001D5A8 /* MPSelectedAttachmentTableCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C5FE9AD17843CE20001D5A8 /* MPSelectedAttachmentTableCellView.m */; };
|
||||||
@@ -526,6 +528,9 @@
|
|||||||
4C586F9D16D07ABD00E7DB57 /* 00_PasswordTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 00_PasswordTemplate.pdf; sourceTree = "<group>"; };
|
4C586F9D16D07ABD00E7DB57 /* 00_PasswordTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 00_PasswordTemplate.pdf; sourceTree = "<group>"; };
|
||||||
4C586F9F16D07D7200E7DB57 /* 01_PackageNetworkTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 01_PackageNetworkTemplate.pdf; sourceTree = "<group>"; };
|
4C586F9F16D07D7200E7DB57 /* 01_PackageNetworkTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 01_PackageNetworkTemplate.pdf; sourceTree = "<group>"; };
|
||||||
4C586FA116D07F6A00E7DB57 /* 02_MessageBoxWarningTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 02_MessageBoxWarningTemplate.pdf; sourceTree = "<group>"; };
|
4C586FA116D07F6A00E7DB57 /* 02_MessageBoxWarningTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 02_MessageBoxWarningTemplate.pdf; sourceTree = "<group>"; };
|
||||||
|
4C58A4A12192EC1600B13370 /* NSIndexPath+MPAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSIndexPath+MPAdditions.h"; sourceTree = "<group>"; };
|
||||||
|
4C58A4A22192EC1600B13370 /* NSIndexPath+MPAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSIndexPath+MPAdditions.m"; sourceTree = "<group>"; };
|
||||||
|
4C58A4A72192EEBE00B13370 /* MPTestIndexPathAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPTestIndexPathAdditions.m; sourceTree = "<group>"; };
|
||||||
4C5A11FB1708DE8700223D8A /* MPPasswordCreatorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCreatorViewController.h; sourceTree = "<group>"; };
|
4C5A11FB1708DE8700223D8A /* MPPasswordCreatorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCreatorViewController.h; sourceTree = "<group>"; };
|
||||||
4C5A11FC1708DE8700223D8A /* MPPasswordCreatorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordCreatorViewController.m; sourceTree = "<group>"; };
|
4C5A11FC1708DE8700223D8A /* MPPasswordCreatorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordCreatorViewController.m; sourceTree = "<group>"; };
|
||||||
4C5ADC3017830B09004E1E8D /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InspectorView.strings; sourceTree = "<group>"; };
|
4C5ADC3017830B09004E1E8D /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InspectorView.strings; sourceTree = "<group>"; };
|
||||||
@@ -1085,6 +1090,8 @@
|
|||||||
4C0949581FD6B89B004F2971 /* NSUserNotification+MPAdditions.m */,
|
4C0949581FD6B89B004F2971 /* NSUserNotification+MPAdditions.m */,
|
||||||
4C769CA7213D59BF00A3F60A /* KPKEntry+MPCustomAttributeProperties.h */,
|
4C769CA7213D59BF00A3F60A /* KPKEntry+MPCustomAttributeProperties.h */,
|
||||||
4C769CA8213D59BF00A3F60A /* KPKEntry+MPCustomAttributeProperties.m */,
|
4C769CA8213D59BF00A3F60A /* KPKEntry+MPCustomAttributeProperties.m */,
|
||||||
|
4C58A4A12192EC1600B13370 /* NSIndexPath+MPAdditions.h */,
|
||||||
|
4C58A4A22192EC1600B13370 /* NSIndexPath+MPAdditions.m */,
|
||||||
);
|
);
|
||||||
name = Categories;
|
name = Categories;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1213,6 +1220,7 @@
|
|||||||
4C8F0C721FCF1B7A00BE157F /* MPTestPickcharsParser.m */,
|
4C8F0C721FCF1B7A00BE157F /* MPTestPickcharsParser.m */,
|
||||||
4C71BCB32167B75900B4CBDA /* MPTestPluginVersionComparator.m */,
|
4C71BCB32167B75900B4CBDA /* MPTestPluginVersionComparator.m */,
|
||||||
4C45FB1F178E09ED0010007D /* Supporting Files */,
|
4C45FB1F178E09ED0010007D /* Supporting Files */,
|
||||||
|
4C58A4A72192EEBE00B13370 /* MPTestIndexPathAdditions.m */,
|
||||||
);
|
);
|
||||||
path = MacPassTests;
|
path = MacPassTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1939,6 +1947,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
4C58A4A82192EEBE00B13370 /* MPTestIndexPathAdditions.m in Sources */,
|
||||||
4C45FB2D178E0BCB0010007D /* MPDatabaseLoading.m in Sources */,
|
4C45FB2D178E0BCB0010007D /* MPDatabaseLoading.m in Sources */,
|
||||||
4C8DEAA21C314D2C00D24C32 /* MPTestAutotypeDelay.m in Sources */,
|
4C8DEAA21C314D2C00D24C32 /* MPTestAutotypeDelay.m in Sources */,
|
||||||
4C71BCB42167B75900B4CBDA /* MPTestPluginVersionComparator.m in Sources */,
|
4C71BCB42167B75900B4CBDA /* MPTestPluginVersionComparator.m in Sources */,
|
||||||
@@ -2039,6 +2048,7 @@
|
|||||||
4C3666411787327E00B249F1 /* MPDocument+Attachments.m in Sources */,
|
4C3666411787327E00B249F1 /* MPDocument+Attachments.m in Sources */,
|
||||||
4CF6C3021FBF39BF0055AD03 /* MPPluginTabelCellView.m in Sources */,
|
4CF6C3021FBF39BF0055AD03 /* MPPluginTabelCellView.m in Sources */,
|
||||||
4C10412C178CDD44001B5239 /* NSDate+Humanized.m in Sources */,
|
4C10412C178CDD44001B5239 /* NSDate+Humanized.m in Sources */,
|
||||||
|
4C58A4A32192EC1600B13370 /* NSIndexPath+MPAdditions.m in Sources */,
|
||||||
4C2F17A21FD69BCA0097418D /* MPUserNotificationCenterDelegate.m in Sources */,
|
4C2F17A21FD69BCA0097418D /* MPUserNotificationCenterDelegate.m in Sources */,
|
||||||
4CC663E7216F7A7100E33965 /* MPPluginRepositoryBrowserViewController.m in Sources */,
|
4CC663E7216F7A7100E33965 /* MPPluginRepositoryBrowserViewController.m in Sources */,
|
||||||
4C0C59F118B17F10009C7B76 /* DDHotKeyUtilities.m in Sources */,
|
4C0C59F118B17F10009C7B76 /* DDHotKeyUtilities.m in Sources */,
|
||||||
|
|||||||
@@ -39,4 +39,8 @@
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)tableView:(NSTableView *)tableView draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint forRowIndexes:(nonnull NSIndexSet *)rowIndexes {
|
||||||
|
session.draggingFormation = NSDraggingFormationList;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ NSString *const _MPTableSecurCellView = @"PasswordCell";
|
|||||||
self.entryTable.doubleAction = @selector(_columnDoubleClick:);
|
self.entryTable.doubleAction = @selector(_columnDoubleClick:);
|
||||||
self.entryTable.target = self;
|
self.entryTable.target = self;
|
||||||
self.entryTable.floatsGroupRows = NO;
|
self.entryTable.floatsGroupRows = NO;
|
||||||
[self.entryTable registerForDraggedTypes:@[KPKEntryUTI]];
|
[self.entryTable registerForDraggedTypes:@[KPKEntryUTI, KPKEntryUUDIUTI]];
|
||||||
/* First responder notifications */
|
/* First responder notifications */
|
||||||
[NSNotificationCenter.defaultCenter addObserver:self
|
[NSNotificationCenter.defaultCenter addObserver:self
|
||||||
selector:@selector(_didBecomFirstResponder:)
|
selector:@selector(_didBecomFirstResponder:)
|
||||||
|
|||||||
@@ -23,16 +23,13 @@
|
|||||||
#import "MPOutlineDataSource.h"
|
#import "MPOutlineDataSource.h"
|
||||||
#import "MPDocument.h"
|
#import "MPDocument.h"
|
||||||
#import "MPConstants.h"
|
#import "MPConstants.h"
|
||||||
|
#import "NSIndexPath+MPAdditions.h"
|
||||||
|
|
||||||
#import "KeePassKit/KeePassKit.h"
|
#import "KeePassKit/KeePassKit.h"
|
||||||
|
|
||||||
@interface MPOutlineDataSource ()
|
@interface MPOutlineDataSource ()
|
||||||
|
|
||||||
@property (weak) KPKGroup *localDraggedGroup;
|
|
||||||
@property (weak) KPKEntry *localDraggedEntry;
|
|
||||||
|
|
||||||
@property (strong) NSArray<KPKGroup *> *draggedGroups;
|
@property (strong) NSArray<KPKGroup *> *draggedGroups;
|
||||||
@property (strong) NSArray<KPKEntry *> *draggedEntries;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -47,74 +44,88 @@
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id<NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index {
|
- (void)outlineView:(NSOutlineView *)outlineView draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint forItems:(NSArray *)draggedItems {
|
||||||
|
session.draggingFormation = NSDraggingFormationList;
|
||||||
/* Clean up our local search */
|
self.draggedGroups = @[];
|
||||||
self.localDraggedEntry = nil;
|
NSMutableArray *localDraggedGroups = [[NSMutableArray alloc] init];
|
||||||
self.localDraggedGroup = nil;
|
for(NSTreeNode *node in draggedItems) {
|
||||||
|
BOOL addNode = YES;
|
||||||
info.animatesToDestination = YES;
|
for(NSTreeNode *otherNode in draggedItems) {
|
||||||
NSDragOperation operationMask = NSDragOperationMove;
|
if(node == otherNode) {
|
||||||
/*
|
continue;
|
||||||
If we can support copy on drag, this can be used
|
}
|
||||||
to obtain the dragging modifier mask the user presses
|
addNode &= ![otherNode.indexPath containsIndexPath:node.indexPath];
|
||||||
*/
|
}
|
||||||
BOOL localCopy = NO;
|
if(addNode) {
|
||||||
if([info draggingSourceOperationMask] == NSDragOperationCopy) {
|
KPKGroup *group = node.representedObject;
|
||||||
operationMask = NSDragOperationCopy;
|
[localDraggedGroups addObject:group];
|
||||||
localCopy = YES;
|
}
|
||||||
}
|
}
|
||||||
|
self.draggedGroups = [localDraggedGroups copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id<NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index {
|
||||||
|
info.animatesToDestination = YES;
|
||||||
|
|
||||||
|
|
||||||
/* Check if the Target is the root group */
|
|
||||||
id targetItem = [item representedObject];
|
id targetItem = [item representedObject];
|
||||||
if( ![targetItem isKindOfClass:KPKGroup.class] ) {
|
if( ![targetItem isKindOfClass:KPKGroup.class] ) {
|
||||||
return NSDragOperationNone; // Block all unknown types
|
return NSDragOperationNone; // Block all unknown types
|
||||||
}
|
}
|
||||||
|
|
||||||
NSPasteboard *pasteBoard = [info draggingPasteboard];
|
BOOL copyDrag = (info.draggingSourceOperationMask == NSDragOperationCopy);
|
||||||
KPKGroup *draggedGroup = nil;
|
|
||||||
KPKEntry *draggedEntry = nil;
|
|
||||||
BOOL couldReadPasteboard = [self _readDataFromPasteboard:pasteBoard group:&draggedGroup entry:&draggedEntry];
|
|
||||||
if(!couldReadPasteboard) {
|
|
||||||
return NSDragOperationNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
KPKGroup *targetGroup = targetItem;
|
KPKGroup *targetGroup = targetItem;
|
||||||
BOOL validTarget = YES;
|
BOOL isGroupDrop = (info.draggingSource == outlineView);
|
||||||
MPDocument *document = outlineView.window.windowController.document;
|
BOOL isEntryDrop = (!isGroupDrop && [info.draggingSource window] == outlineView.window);
|
||||||
/* Dragging Groups */
|
if(isGroupDrop) {
|
||||||
if(draggedGroup) {
|
/* local group drop */
|
||||||
self.localDraggedGroup = [document findGroup:draggedGroup.uuid];
|
for(KPKGroup *draggedGroup in self.draggedGroups) {
|
||||||
if( [draggedGroup.uuid isEqual:targetGroup.uuid] ) {
|
BOOL validTarget = YES;
|
||||||
return NSDragOperationNone; // Groups cannot be moved inside themselves
|
if(targetGroup == draggedGroup) {
|
||||||
}
|
return copyDrag ? NSDragOperationCopy : NSDragOperationNone;
|
||||||
if(self.localDraggedGroup) {
|
|
||||||
if( self.localDraggedGroup.parent == targetGroup ) {
|
|
||||||
validTarget &= index != NSOutlineViewDropOnItemIndex;
|
|
||||||
validTarget &= index != [self.localDraggedGroup.parent.groups indexOfObject:self.localDraggedGroup];
|
|
||||||
}
|
}
|
||||||
BOOL isAnchesor = [self.localDraggedGroup isAnchestorOf:targetGroup];
|
|
||||||
|
if(draggedGroup.parent == targetGroup) {
|
||||||
|
if(index == NSOutlineViewDropOnItemIndex) {
|
||||||
|
return copyDrag ? NSDragOperationCopy : NSDragOperationNone;
|
||||||
|
}
|
||||||
|
validTarget &= index != draggedGroup.index;
|
||||||
|
}
|
||||||
|
BOOL isAnchesor = [draggedGroup isAnchestorOf:targetGroup];
|
||||||
validTarget &= !isAnchesor;
|
validTarget &= !isAnchesor;
|
||||||
|
if(!validTarget) {
|
||||||
|
return NSDragOperationNone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copyDrag ? NSDragOperationCopy : NSDragOperationMove;
|
||||||
|
}
|
||||||
|
else if(isEntryDrop) {
|
||||||
|
/* local entry drop */
|
||||||
|
MPDocument *document = outlineView.window.windowController.document;
|
||||||
|
NSUUID *entryUUID = [self _entryUUIDsFromPasteboard:info.draggingPasteboard].firstObject;
|
||||||
|
KPKEntry *entry = [document findEntry:entryUUID];
|
||||||
|
// FIXME: set correct item when drop is proposed between items
|
||||||
|
if(index != NSOutlineViewDropOnItemIndex) {
|
||||||
|
NSTreeNode *node = item;
|
||||||
|
NSUInteger dropIndex = MIN(node.childNodes.count-1, index);
|
||||||
|
id dropItem = node.childNodes[dropIndex];
|
||||||
|
[outlineView setDropItem:dropItem dropChildIndex:NSOutlineViewDropOnItemIndex];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* Copy can always work in this case */
|
|
||||||
operationMask = NSDragOperationCopy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(draggedEntry) {
|
|
||||||
self.localDraggedEntry = [document findEntry:draggedEntry.uuid];
|
|
||||||
if(self.localDraggedEntry) {
|
|
||||||
/* local Copy is always valid regardless of parent */
|
|
||||||
validTarget = localCopy ? YES : self.localDraggedEntry.parent != targetGroup;
|
|
||||||
[outlineView setDropItem:item dropChildIndex:NSOutlineViewDropOnItemIndex];
|
[outlineView setDropItem:item dropChildIndex:NSOutlineViewDropOnItemIndex];
|
||||||
}
|
}
|
||||||
else {
|
if(entry.parent == targetItem) {
|
||||||
/* Entry copy is always valid */
|
return copyDrag ? NSDragOperationCopy : NSDragOperationNone;
|
||||||
operationMask = NSDragOperationCopy;
|
|
||||||
}
|
}
|
||||||
|
return copyDrag ? NSDragOperationCopy : NSDragOperationMove;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* extern drop */
|
||||||
|
if([info.draggingPasteboard.types containsObject:KPKEntryUUDIUTI]) {
|
||||||
|
[outlineView setDropItem:item dropChildIndex:NSOutlineViewDropOnItemIndex];
|
||||||
|
}
|
||||||
|
return NSDragOperationCopy;
|
||||||
}
|
}
|
||||||
return validTarget ? operationMask : NSDragOperationNone;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id<NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index {
|
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id<NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index {
|
||||||
@@ -125,80 +136,99 @@
|
|||||||
return NO; // Wrong
|
return NO; // Wrong
|
||||||
}
|
}
|
||||||
|
|
||||||
NSPasteboard *pasteBoard = [info draggingPasteboard];
|
BOOL copyItem = (info.draggingSourceOperationMask == NSDragOperationCopy);
|
||||||
KPKGroup *draggedGroup = nil;
|
|
||||||
KPKEntry *draggedEntry = nil;
|
|
||||||
BOOL validPateboard = [self _readDataFromPasteboard:pasteBoard group:&draggedGroup entry:&draggedEntry];
|
|
||||||
if(!validPateboard) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL copyItem = ([info draggingSourceOperationMask] == NSDragOperationCopy);
|
KPKGroup *targetGroup = targetItem;
|
||||||
|
MPDocument *document = outlineView.window.windowController.document;
|
||||||
KPKGroup *targetGroup = (KPKGroup *)targetItem;
|
if(info.draggingSource == outlineView) {
|
||||||
if(draggedGroup) {
|
if(copyItem) {
|
||||||
if(copyItem || (nil == self.localDraggedGroup) ) {
|
NSUInteger insertIndex = index;
|
||||||
draggedGroup = [draggedGroup copyWithTitle:nil options:kKPKCopyOptionNone];
|
for(KPKGroup *group in self.draggedGroups.reverseObjectEnumerator) {
|
||||||
[draggedGroup addToGroup:targetGroup atIndex:index];
|
KPKGroup *groupCopy = [group copyWithTitle:nil options:kKPKCopyOptionNone];
|
||||||
[draggedGroup.undoManager setActionName:NSLocalizedString(@"COPY_GROUP", "Action title for copying a group via drag and drop")];
|
[groupCopy addToGroup:targetGroup atIndex:insertIndex];
|
||||||
|
insertIndex = groupCopy.index;
|
||||||
|
[groupCopy.undoManager setActionName:NSLocalizedString(@"COPY_GROUP", "Action title for copying a group via drag and drop")];
|
||||||
|
}
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
else if(self.localDraggedGroup) {
|
else {
|
||||||
/* Simple move */
|
NSUInteger insertIndex = index;
|
||||||
[self.localDraggedGroup moveToGroup:targetGroup atIndex:index];
|
for(KPKGroup *group in self.draggedGroups.reverseObjectEnumerator) {
|
||||||
[self.localDraggedGroup.undoManager setActionName:NSLocalizedString(@"MOVE_GROUP", "Action title for moving a group via drag and drop")];
|
[group moveToGroup:targetGroup atIndex:insertIndex];
|
||||||
return YES;
|
insertIndex = group.index;
|
||||||
}
|
[group.undoManager setActionName:NSLocalizedString(@"MOVE_GROUP", "Action title for moving a group via drag and drop")];
|
||||||
/* Nothing valid */
|
}
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
else if(draggedEntry) {
|
|
||||||
if(copyItem || (nil == self.localDraggedEntry)) {
|
|
||||||
draggedEntry = [draggedEntry copyWithTitle:nil options:kKPKCopyOptionNone];
|
|
||||||
[draggedEntry addToGroup:targetGroup];
|
|
||||||
[draggedEntry.undoManager setActionName:NSLocalizedString(@"COPY_ENTRY", "Action title for copying an entry via drag and drop")];
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
else if(self.localDraggedEntry) {
|
|
||||||
[self.localDraggedEntry moveToGroup:targetGroup];
|
|
||||||
[self.localDraggedEntry.undoManager setActionName:NSLocalizedString(@"MOVE_ENTRY", "Action title for moving an entry via drag and drop")];
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NO;
|
else if([info.draggingSource window] == outlineView.window) {
|
||||||
|
NSArray<NSUUID *> *entryUUIDs = [self _entryUUIDsFromPasteboard:info.draggingPasteboard];
|
||||||
|
if(copyItem) {
|
||||||
|
for(NSUUID *entryUUID in entryUUIDs) {
|
||||||
|
KPKEntry *draggedEntry = [[document findEntry:entryUUID] copyWithTitle:nil options:kKPKCopyOptionNone];
|
||||||
|
[draggedEntry addToGroup:targetGroup];
|
||||||
|
[draggedEntry.undoManager setActionName:NSLocalizedString(@"COPY_ENTRY", "Action title for copying an entry via drag and drop")];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for(NSUUID *entryUUID in entryUUIDs) {
|
||||||
|
KPKEntry *draggedEntry = [document findEntry:entryUUID];
|
||||||
|
[draggedEntry moveToGroup:targetGroup];
|
||||||
|
[draggedEntry.undoManager setActionName:NSLocalizedString(@"MOVE_ENTRY", "Action title for moving an entry via drag and drop")];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
for(KPKEntry *draggedEntry in [self _entriesFromPasteboard:info.draggingPasteboard]) {
|
||||||
|
[draggedEntry addToGroup:targetGroup];
|
||||||
|
[draggedEntry.undoManager setActionName:NSLocalizedString(@"DRAG_ENTRY", "Action title for copying an entry via drag and drop to another database")];
|
||||||
|
}
|
||||||
|
for(KPKGroup *draggedGroup in [self _normalizedGroupsFromPasterboard:info.draggingPasteboard]) {
|
||||||
|
[draggedGroup addToGroup:targetGroup];
|
||||||
|
[draggedGroup.undoManager setActionName:NSLocalizedString(@"DRAG_GROUP", "Actiontitle for copying groups via drag and drop to antother database")];
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_readDataFromPasteboard:(NSPasteboard *)pasteboard group:(KPKGroup **)group entry:(KPKEntry **)entry;{
|
- (NSArray<NSUUID *> *)_entryUUIDsFromPasteboard:(NSPasteboard *)pBoard {
|
||||||
|
if([pBoard.types containsObject:KPKEntryUUDIUTI]) {
|
||||||
if(entry == NULL || group == NULL) {
|
if([pBoard canReadObjectForClasses:@[NSUUID.class] options:nil]) {
|
||||||
return NO; // Need valid pointers
|
return [pBoard readObjectsForClasses:@[NSUUID.class] options:nil];
|
||||||
}
|
|
||||||
/* Cleanup old stuff */
|
|
||||||
|
|
||||||
NSArray *types = pasteboard.types;
|
|
||||||
if(types.count > 1 || types.count == 0) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *draggedType = types.lastObject;
|
|
||||||
if([draggedType isEqualToString:KPKGroupUTI]) {
|
|
||||||
// dragging group
|
|
||||||
NSArray *groups = [pasteboard readObjectsForClasses:@[KPKGroup.class] options:nil];
|
|
||||||
if(groups.count != 1) {
|
|
||||||
return NO;
|
|
||||||
}
|
}
|
||||||
*group = groups.lastObject;
|
|
||||||
return YES;
|
|
||||||
}
|
}
|
||||||
else if([draggedType isEqualToString:KPKEntryUTI]) {
|
return @[];
|
||||||
NSArray *entries = [pasteboard readObjectsForClasses:@[KPKEntry.class] options:nil];
|
}
|
||||||
if([entries count] != 1) {
|
|
||||||
return NO; // NO entry readable
|
- (NSArray<KPKGroup *> *)_normalizedGroupsFromPasterboard:(NSPasteboard *)pBoard {
|
||||||
|
if([pBoard.types containsObject:KPKGroupUTI]) {
|
||||||
|
if([pBoard canReadObjectForClasses:@[KPKGroup.class] options:nil]) {
|
||||||
|
NSArray<KPKGroup *> *groups = [pBoard readObjectsForClasses:@[KPKGroup.class] options:nil];
|
||||||
|
return [groups filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
||||||
|
KPKGroup *group = evaluatedObject;
|
||||||
|
BOOL isValid = YES;
|
||||||
|
for(KPKGroup *otherGroup in groups) {
|
||||||
|
if(otherGroup == group) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(nil != [otherGroup groupForUUID:group.uuid]) {
|
||||||
|
isValid = NO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isValid;
|
||||||
|
}]];
|
||||||
}
|
}
|
||||||
*entry = entries.lastObject;
|
|
||||||
return YES;
|
|
||||||
}
|
}
|
||||||
return NO;
|
return @[];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<KPKEntry *> *)_entriesFromPasteboard:(NSPasteboard *)pBoard {
|
||||||
|
if([pBoard.types containsObject:KPKEntryUTI]) {
|
||||||
|
if([pBoard canReadObjectForClasses:@[KPKEntry.class] options:nil]) {
|
||||||
|
return [pBoard readObjectsForClasses:@[KPKEntry.class] options:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return @[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ NSString *const _MPOutlinveViewHeaderViewIdentifier = @"HeaderCell";
|
|||||||
self.outlineView.doubleAction = @selector(_doubleClickedGroup:);
|
self.outlineView.doubleAction = @selector(_doubleClickedGroup:);
|
||||||
self.outlineView.allowsMultipleSelection = YES;
|
self.outlineView.allowsMultipleSelection = YES;
|
||||||
self.outlineView.delegate = self;
|
self.outlineView.delegate = self;
|
||||||
[self.outlineView registerForDraggedTypes:@[ KPKGroupUTI, KPKEntryUTI ]];
|
[self.outlineView registerForDraggedTypes:@[ KPKGroupUTI, KPKGroupUUIDUTI, KPKEntryUTI, KPKEntryUUDIUTI ]];
|
||||||
[self.outlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
|
[self.outlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
|
||||||
|
|
||||||
[NSNotificationCenter.defaultCenter addObserver:self
|
[NSNotificationCenter.defaultCenter addObserver:self
|
||||||
|
|||||||
19
MacPass/NSIndexPath+MPAdditions.h
Normal file
19
MacPass/NSIndexPath+MPAdditions.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// NSIndexPath+MPAdditions.h
|
||||||
|
// MacPass
|
||||||
|
//
|
||||||
|
// Created by Michael Starke on 07.11.18.
|
||||||
|
// Copyright © 2018 HicknHack Software GmbH. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface NSIndexPath (MPAdditions)
|
||||||
|
|
||||||
|
- (BOOL)containsIndexPath:(NSIndexPath *)path;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
34
MacPass/NSIndexPath+MPAdditions.m
Normal file
34
MacPass/NSIndexPath+MPAdditions.m
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// NSIndexPath+MPAdditions.m
|
||||||
|
// MacPass
|
||||||
|
//
|
||||||
|
// Created by Michael Starke on 07.11.18.
|
||||||
|
// Copyright © 2018 HicknHack Software GmbH. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "NSIndexPath+MPAdditions.h"
|
||||||
|
|
||||||
|
@implementation NSIndexPath (MPAdditions)
|
||||||
|
|
||||||
|
- (BOOL)containsIndexPath:(NSIndexPath *)path {
|
||||||
|
NSComparisonResult result = [self compare:path];
|
||||||
|
if(result == NSOrderedSame) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
if(result == NSOrderedDescending) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
if(self.length == path.length) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSUInteger commonLength = MIN(self.length, path.length);
|
||||||
|
for(NSUInteger position = 0; position < commonLength; position++) {
|
||||||
|
if([self indexAtPosition:position] != [path indexAtPosition:position]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
53
MacPassTests/MPTestIndexPathAdditions.m
Normal file
53
MacPassTests/MPTestIndexPathAdditions.m
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// MPTestIndexPathAdditions.m
|
||||||
|
// MacPassTests
|
||||||
|
//
|
||||||
|
// Created by Michael Starke on 07.11.18.
|
||||||
|
// Copyright © 2018 HicknHack Software GmbH. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
#import "NSIndexPath+MPAdditions.h"
|
||||||
|
|
||||||
|
@interface MPTestIndexPathAdditions : XCTestCase
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MPTestIndexPathAdditions
|
||||||
|
|
||||||
|
- (void)testContainsA {
|
||||||
|
NSUInteger indexes1[] = {0,1,2,3};
|
||||||
|
NSUInteger indexes2[] = {0,1,3};
|
||||||
|
|
||||||
|
NSIndexPath *path1 = [[NSIndexPath alloc] initWithIndexes:indexes1 length:sizeof(indexes1)/sizeof(NSUInteger)];
|
||||||
|
NSIndexPath *path2 = [[NSIndexPath alloc] initWithIndexes:indexes2 length:sizeof(indexes2)/sizeof(NSUInteger)];
|
||||||
|
|
||||||
|
XCTAssertFalse([path1 containsIndexPath:path2]);
|
||||||
|
XCTAssertFalse([path2 containsIndexPath:path1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testContainsB {
|
||||||
|
NSUInteger indexes1[] = {0,2,3};
|
||||||
|
NSUInteger indexes2[] = {0,2};
|
||||||
|
|
||||||
|
NSIndexPath *path1 = [[NSIndexPath alloc] initWithIndexes:indexes1 length:sizeof(indexes1)/sizeof(NSUInteger)];
|
||||||
|
NSIndexPath *path2 = [[NSIndexPath alloc] initWithIndexes:indexes2 length:sizeof(indexes2)/sizeof(NSUInteger)];
|
||||||
|
|
||||||
|
XCTAssertFalse([path1 containsIndexPath:path2]);
|
||||||
|
XCTAssertTrue([path2 containsIndexPath:path1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testContainsC {
|
||||||
|
NSUInteger indexes1[] = {10,1,3};
|
||||||
|
NSUInteger indexes2[] = {10,1,3};
|
||||||
|
|
||||||
|
NSIndexPath *path1 = [[NSIndexPath alloc] initWithIndexes:indexes1 length:sizeof(indexes1)/sizeof(NSUInteger)];
|
||||||
|
NSIndexPath *path2 = [[NSIndexPath alloc] initWithIndexes:indexes2 length:sizeof(indexes2)/sizeof(NSUInteger)];
|
||||||
|
|
||||||
|
XCTAssertTrue([path1 containsIndexPath:path2]);
|
||||||
|
XCTAssertTrue([path2 containsIndexPath:path1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
||||||
Reference in New Issue
Block a user