From 9c69bd652899c41677c9cbae76b09974996f76fb Mon Sep 17 00:00:00 2001 From: michael starke Date: Thu, 15 Aug 2013 22:45:54 +0200 Subject: [PATCH] Experiments with globe hot-key via EventTaps instead of Carbon API Drag and Drop of entries and groups reworked Minor fixes in Tests --- KeePassKit | 2 +- MacPass.xcodeproj/project.pbxproj | 24 ++++-- MacPass/KdbGroup+MPTreeTools.m | 6 +- MacPass/KdbGroup+Undo.m | 6 ++ MacPass/MPAppDelegate.m | 3 + MacPass/MPAutotypeDaemon.h | 16 ++++ MacPass/MPAutotypeDaemon.m | 79 +++++++++++++++++++ MacPass/MPOutlineDataSource.m | 72 ++++++++--------- ...TextPlaceholder.h => KPKTestPlaceholder.h} | 0 ...TextPlaceholder.m => KPKTestPlaceholder.m} | 7 +- 10 files changed, 162 insertions(+), 53 deletions(-) create mode 100644 MacPass/MPAutotypeDaemon.h create mode 100644 MacPass/MPAutotypeDaemon.m rename MacPassTests/{KPKTextPlaceholder.h => KPKTestPlaceholder.h} (100%) rename MacPassTests/{KPKTextPlaceholder.m => KPKTestPlaceholder.m} (89%) diff --git a/KeePassKit b/KeePassKit index 1ccbacd7..bededcbf 160000 --- a/KeePassKit +++ b/KeePassKit @@ -1 +1 @@ -Subproject commit 1ccbacd760a94a1e7989229b767a601ce24a9a56 +Subproject commit bededcbfb8758374a710ca67cbb059ad066f42e6 diff --git a/MacPass.xcodeproj/project.pbxproj b/MacPass.xcodeproj/project.pbxproj index e974866b..49dd8191 100644 --- a/MacPass.xcodeproj/project.pbxproj +++ b/MacPass.xcodeproj/project.pbxproj @@ -185,7 +185,7 @@ 4C6D1D25178579570014C5A5 /* 48_FolderTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4C6D1D24178579570014C5A5 /* 48_FolderTemplate.pdf */; }; 4C6D1D27178586CA0014C5A5 /* 99_AddFolderTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4C6D1D26178586CA0014C5A5 /* 99_AddFolderTemplate.pdf */; }; 4C6D1D2B17858A250014C5A5 /* MacPass.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4C6D1D2A17858A250014C5A5 /* MacPass.icns */; }; - 4C6FDD2117BC4F4C004AEEC8 /* KPKTextPlaceholder.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6FDD2017BC4F4C004AEEC8 /* KPKTextPlaceholder.m */; }; + 4C6FDD2117BC4F4C004AEEC8 /* KPKTestPlaceholder.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6FDD2017BC4F4C004AEEC8 /* KPKTestPlaceholder.m */; }; 4C701CBC178618A000581B88 /* 12_RemoteTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4C701CBB178618A000581B88 /* 12_RemoteTemplate.pdf */; }; 4C70D100179092F200652EE9 /* KPKPassword.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C70D0FF179092F200652EE9 /* KPKPassword.m */; }; 4C74DD07177BD1640034A9DB /* MPCustomFieldView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C74DD06177BD1640034A9DB /* MPCustomFieldView.m */; }; @@ -231,6 +231,8 @@ 4CA0B2FC15BCAF8600654E32 /* MPSettingsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA0B2FB15BCAF8600654E32 /* MPSettingsWindowController.m */; }; 4CA0E3A7176FAF99004D18CB /* MPDocumentQueryService.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA0E3A6176FAF99004D18CB /* MPDocumentQueryService.m */; }; 4CA2335A176DBFE100F0B6AC /* MPLockDaemon.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA23359176DBFE100F0B6AC /* MPLockDaemon.m */; }; + 4CA2E2DB17BCDA8300714076 /* NSString+Placeholder.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2E2DA17BCDA8300714076 /* NSString+Placeholder.m */; }; + 4CA2E2DE17BCDEAE00714076 /* MPAutotypeDaemon.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2E2DD17BCDEAE00714076 /* MPAutotypeDaemon.m */; }; 4CAC614317AD319200023F9E /* KPKTestXmlParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CAC614217AD319200023F9E /* KPKTestXmlParsing.m */; }; 4CAD747B15B887FD00104512 /* DDXMLElementAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CAD745715B887FD00104512 /* DDXMLElementAdditions.m */; }; 4CAD747C15B887FD00104512 /* NSString+DDXML.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CAD745A15B887FD00104512 /* NSString+DDXML.m */; }; @@ -636,8 +638,8 @@ 4C6D1D24178579570014C5A5 /* 48_FolderTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 48_FolderTemplate.pdf; sourceTree = ""; }; 4C6D1D26178586CA0014C5A5 /* 99_AddFolderTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = 99_AddFolderTemplate.pdf; path = Icons/99_AddFolderTemplate.pdf; sourceTree = ""; }; 4C6D1D2A17858A250014C5A5 /* MacPass.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = MacPass.icns; path = "../Assets/App icon/MacPass.icns"; sourceTree = ""; }; - 4C6FDD1F17BC4F4C004AEEC8 /* KPKTextPlaceholder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KPKTextPlaceholder.h; sourceTree = ""; }; - 4C6FDD2017BC4F4C004AEEC8 /* KPKTextPlaceholder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KPKTextPlaceholder.m; sourceTree = ""; }; + 4C6FDD1F17BC4F4C004AEEC8 /* KPKTestPlaceholder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KPKTestPlaceholder.h; sourceTree = ""; }; + 4C6FDD2017BC4F4C004AEEC8 /* KPKTestPlaceholder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KPKTestPlaceholder.m; sourceTree = ""; }; 4C701CBB178618A000581B88 /* 12_RemoteTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 12_RemoteTemplate.pdf; sourceTree = ""; }; 4C70D0FE179092F200652EE9 /* KPKPassword.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KPKPassword.h; sourceTree = ""; }; 4C70D0FF179092F200652EE9 /* KPKPassword.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = KPKPassword.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; @@ -720,6 +722,10 @@ 4CA0E3A6176FAF99004D18CB /* MPDocumentQueryService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPDocumentQueryService.m; sourceTree = ""; }; 4CA23358176DBFE100F0B6AC /* MPLockDaemon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLockDaemon.h; sourceTree = ""; }; 4CA23359176DBFE100F0B6AC /* MPLockDaemon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLockDaemon.m; sourceTree = ""; }; + 4CA2E2D917BCDA8300714076 /* NSString+Placeholder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Placeholder.h"; sourceTree = ""; }; + 4CA2E2DA17BCDA8300714076 /* NSString+Placeholder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Placeholder.m"; sourceTree = ""; }; + 4CA2E2DC17BCDEAE00714076 /* MPAutotypeDaemon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAutotypeDaemon.h; sourceTree = ""; }; + 4CA2E2DD17BCDEAE00714076 /* MPAutotypeDaemon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAutotypeDaemon.m; sourceTree = ""; }; 4CAAFDD51787AED60013FCF9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = ""; }; 4CAC614117AD319200023F9E /* KPKTestXmlParsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KPKTestXmlParsing.h; sourceTree = ""; }; 4CAC614217AD319200023F9E /* KPKTestXmlParsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KPKTestXmlParsing.m; sourceTree = ""; }; @@ -1169,6 +1175,8 @@ 4C2E382016D141F700037A9D /* Helper */ = { isa = PBXGroup; children = ( + 4CA2E2DC17BCDEAE00714076 /* MPAutotypeDaemon.h */, + 4CA2E2DD17BCDEAE00714076 /* MPAutotypeDaemon.m */, 4C5BF679175C01F300D53DF7 /* MPUppercaseStringValueTransformer.h */, 4C5BF67A175C01F300D53DF7 /* MPUppercaseStringValueTransformer.m */, 4CF6C70F176F4533007A811D /* MPStringLengthValueTransformer.h */, @@ -1276,8 +1284,8 @@ 4CE76DAC17B3AD010043B82B /* KPKHashedDataTest.m */, 4CEAF85917BA9B44001307A6 /* KPKTestKeyfileParsing.h */, 4CEAF85A17BA9B44001307A6 /* KPKTestKeyfileParsing.m */, - 4C6FDD1F17BC4F4C004AEEC8 /* KPKTextPlaceholder.h */, - 4C6FDD2017BC4F4C004AEEC8 /* KPKTextPlaceholder.m */, + 4C6FDD1F17BC4F4C004AEEC8 /* KPKTestPlaceholder.h */, + 4C6FDD2017BC4F4C004AEEC8 /* KPKTestPlaceholder.m */, ); path = MacPassTests; sourceTree = ""; @@ -1666,6 +1674,8 @@ 4C1842C2179B69E700E2F5BC /* NSData+HashedData.m */, 4C6366AA17AF1E0100AAF17D /* NSColor+KeePassKit.h */, 4C6366AB17AF1E0100AAF17D /* NSColor+KeePassKit.m */, + 4CA2E2D917BCDA8300714076 /* NSString+Placeholder.h */, + 4CA2E2DA17BCDA8300714076 /* NSString+Placeholder.m */, ); path = Categories; sourceTree = ""; @@ -2029,7 +2039,7 @@ 4C6366AF17AF207600AAF17D /* KPKTestHexColor.m in Sources */, 4CE76DAD17B3AD010043B82B /* KPKHashedDataTest.m in Sources */, 4CEAF85B17BA9B44001307A6 /* KPKTestKeyfileParsing.m in Sources */, - 4C6FDD2117BC4F4C004AEEC8 /* KPKTextPlaceholder.m in Sources */, + 4C6FDD2117BC4F4C004AEEC8 /* KPKTestPlaceholder.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2243,6 +2253,8 @@ 4C57AE1417BA422B00CA4F34 /* MPSegmentedContextCell.m in Sources */, 4C68456A17BC21DD00FCDBFC /* KPKAutotype.m in Sources */, 4C68456D17BC227B00FCDBFC /* KPKWindowAssociation.m in Sources */, + 4CA2E2DB17BCDA8300714076 /* NSString+Placeholder.m in Sources */, + 4CA2E2DE17BCDEAE00714076 /* MPAutotypeDaemon.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MacPass/KdbGroup+MPTreeTools.m b/MacPass/KdbGroup+MPTreeTools.m index 4412fa68..5edb4b9f 100644 --- a/MacPass/KdbGroup+MPTreeTools.m +++ b/MacPass/KdbGroup+MPTreeTools.m @@ -53,9 +53,9 @@ if(group == nil) { return NO; } - KdbGroup *ancestor = self.parent; - while(ancestor.parent) { - if(group == self) { + KdbGroup *ancestor = group.parent; + while(ancestor) { + if(ancestor == self) { return YES; } ancestor = ancestor.parent; diff --git a/MacPass/KdbGroup+Undo.m b/MacPass/KdbGroup+Undo.m index 6e7319f2..0a33a3b0 100644 --- a/MacPass/KdbGroup+Undo.m +++ b/MacPass/KdbGroup+Undo.m @@ -90,6 +90,11 @@ NSString *const MPGroupNameUndoableKey = @"nameUndoable"; if(!self.parent || !group) { return; // No target or origin } + if(self.parent == group) { + // Correct the index to accomodate the removal + index--; + } + NSUInteger oldIndex = [self.parent.groups indexOfObject:self]; if(oldIndex == NSNotFound) { return; // We aren't in our parents groups list. @@ -102,6 +107,7 @@ NSString *const MPGroupNameUndoableKey = @"nameUndoable"; [self.parent removeObjectFromGroupsAtIndex:oldIndex]; index = MIN(index, [group.groups count]); + index = MAX(index, 0 ); [group insertObject:self inGroupsAtIndex:index]; } diff --git a/MacPass/MPAppDelegate.m b/MacPass/MPAppDelegate.m index c33bf586..2c480a2b 100644 --- a/MacPass/MPAppDelegate.m +++ b/MacPass/MPAppDelegate.m @@ -16,12 +16,14 @@ #import "MPStripLineBreaksTransformer.h" #import "MPServerDaemon.h" #import "MPLockDaemon.h" +#import "MPAutotypeDaemon.h" #import "MPDocumentWindowController.h" @interface MPAppDelegate () { @private MPServerDaemon *serverDaemon; MPLockDaemon *lockDaemon; + MPAutotypeDaemon *autotypeDaemon; BOOL _restoredWindows; } @@ -65,6 +67,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification { serverDaemon = [[MPServerDaemon alloc] init]; lockDaemon = [[MPLockDaemon alloc] init]; + //autotypeDaemon = [[MPAutotypeDaemon alloc] init]; BOOL reopen = [[NSUserDefaults standardUserDefaults] boolForKey:kMPSettingsKeyReopenLastDatabaseOnLaunch]; if(reopen && !_restoredWindows) { diff --git a/MacPass/MPAutotypeDaemon.h b/MacPass/MPAutotypeDaemon.h new file mode 100644 index 00000000..71bdc35d --- /dev/null +++ b/MacPass/MPAutotypeDaemon.h @@ -0,0 +1,16 @@ +// +// MPAutotypeDaemon.h +// MacPass +// +// Created by Michael Starke on 15.08.13. +// Copyright (c) 2013 HicknHack Software GmbH. All rights reserved. +// + +#import + +@interface MPAutotypeDaemon : NSObject + +- (void)processEvent; +- (void)sendKeystrokes; + +@end diff --git a/MacPass/MPAutotypeDaemon.m b/MacPass/MPAutotypeDaemon.m new file mode 100644 index 00000000..c625d971 --- /dev/null +++ b/MacPass/MPAutotypeDaemon.m @@ -0,0 +1,79 @@ +// +// MPAutotypeDaemon.m +// MacPass +// +// Created by Michael Starke on 15.08.13. +// Copyright (c) 2013 HicknHack Software GmbH. All rights reserved. +// + +#import "MPAutotypeDaemon.h" + + +static CGEventRef eventCallback(CGEventTapProxy proxy, + CGEventType type, + CGEventRef event, + void *userInfo) { + + MPAutotypeDaemon *daemon = (__bridge MPAutotypeDaemon *)userInfo; + [daemon processEvent]; + CGEventFlags flags = CGEventGetFlags(event); + // Update this to use settings? + // Call into deamon via self pointer? + if(flags & kCGEventFlagMaskCommand && flags) { + NSLog(@"CMD +"); + } + return event; +} + +@interface MPAutotypeDaemon () { + CFMachPortRef _portRef; +} + +@end + + +@implementation MPAutotypeDaemon + +- (id)init { + self = [super init]; + if(self) { + _portRef = CGEventTapCreate(kCGHIDEventTap, + kCGTailAppendEventTap, + kCGEventTapOptionListenOnly, + CGEventMaskBit(kCGEventKeyDown), + &eventCallback, + (__bridge void*)self); + CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(CFAllocatorGetDefault(), _portRef, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes); + CFRelease(source); + } + return self; +} + +- (void)dealloc { + if(_portRef != NULL) { + CFRelease(_portRef); + } +} + +- (void)processEvent { + /* TODO */ +} + +- (void)sendKeystrokes { + /* TODO */ + CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); + + CGEventRef keyDown = CGEventCreateKeyboardEvent(source, 0x12, TRUE); + CGEventSetFlags(keyDown, 0); + CGEventRef keyUp = CGEventCreateKeyboardEvent(source, 0x12, FALSE); + CGEventSetFlags(keyUp, 0); + CGEventPost(kCGAnnotatedSessionEventTap, keyDown); + CGEventPost(kCGAnnotatedSessionEventTap, keyUp); + + CFRelease(keyUp); + CFRelease(keyDown); + CFRelease(source); +} + +@end diff --git a/MacPass/MPOutlineDataSource.m b/MacPass/MPOutlineDataSource.m index b5c5e15c..fdba0a95 100644 --- a/MacPass/MPOutlineDataSource.m +++ b/MacPass/MPOutlineDataSource.m @@ -51,7 +51,7 @@ } */ id targetItem = [item representedObject]; - if(targetItem == nil) { + if(targetItem == nil || [targetItem isKindOfClass:[MPRootAdapter class]]) { return NSDragOperationNone; // no Target } @@ -72,66 +72,58 @@ if([uuids count] != 1) { return NSDragOperationNone; // NO entry readable } + self.draggedGroup = nil; self.draggedEntry = [document findEntry:[uuids lastObject]]; } else { return NSDragOperationNone; // unkonw type } - if(self.draggedGroup && [targetItem isKindOfClass:[MPRootAdapter class]]) { - return NSDragOperationNone; // Drag over group header - } - KdbGroup *targetGroup = targetItem; BOOL validTarget = YES; if(self.draggedGroup) { - NSLog(@"draggin Group %@", self.draggedGroup.name); + if( self.draggedGroup == targetGroup ) { + return NSDragOperationNone; // Groups cannot be moved inside themselves + } if( self.draggedGroup.parent == targetGroup ) { validTarget &= index != NSOutlineViewDropOnItemIndex; validTarget &= index != [self.draggedGroup.parent.groups indexOfObject:self.draggedGroup]; } + BOOL isAnchesor = [self.draggedGroup isAnchestorOfGroup:targetGroup]; + NSLog(@"DraggedGroup:%@ isAnchestor:%d ofTargetGroup:%@", self.draggedGroup.name, isAnchesor, targetGroup.name); + validTarget &= !isAnchesor; } else if(self.draggedEntry) { - NSLog(@"draggin Entry %@", self.draggedEntry.title); validTarget = self.draggedEntry.parent != targetGroup; + [outlineView setDropItem:item dropChildIndex:NSOutlineViewDropOnItemIndex]; } return validTarget ? oprationMask : NSDragOperationNone; } - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id)info item:(id)item childIndex:(NSInteger)index { - self.draggedGroup = nil; - /* - KdbGroup *target = [item representedObject]; - if(self.draggedGroup) { - BOOL accepted = YES; - if( self.draggedGroup.parent == target ) { - accepted &= index != NSOutlineViewDropOnItemIndex; - accepted &= index != [self.draggedGroup.parent.groups indexOfObject:self.draggedGroup]; - } - accepted = ![self.draggedGroup isAnchestorOfGroup:target]; - if( accepted ) { - [self.draggedGroup moveToGroupUndoable:target atIndex:index]; - } - info.animatesToDestination = !accepted; - self.draggedGroup = nil; - return accepted; - } - NSPasteboard *pasteBoard = [info draggingPasteboard]; - NSArray *items = [pasteBoard pasteboardItems]; - if([items count] > 0) { - NSPasteboardItem *item = items[0]; - UUID *uuid = [[UUID alloc] initWithString:[item stringForType:MPPasteBoardType]]; - MPDocument *document = [[[outlineView window] windowController] document]; - KdbGroup *rootGroup = [document root]; - KdbEntry *draggedEntry = [rootGroup entryForUUID:uuid]; - if(draggedEntry) { - if(draggedEntry.parent != target && index == NSOutlineViewDropOnItemIndex) { - [draggedEntry moveToGroupUndoable:target atIndex:index]; - return YES; - } - } - } - */ + info.animatesToDestination = YES; + NSPasteboard *pasteBoard = [info draggingPasteboard]; + NSArray *types = [pasteBoard types]; + if([types count] > 1 || [types count] == 0) { + return NO; // We cannot work with more than one type + } + + id targetItem = [item representedObject]; + if(![targetItem isKindOfClass:[KdbGroup class]]) { + return NO; // Wrong + } + + KdbGroup *targetGroup = (KdbGroup *)targetItem; + + NSString *draggedType = [types lastObject]; + if([draggedType isEqualToString:MPGroupUTI]) { + [self.draggedGroup moveToGroupUndoable:targetGroup atIndex:index]; + return YES; + } + else if([draggedType isEqualToString:MPUUIDUTI]) { + [self.draggedEntry moveToGroupUndoable:targetGroup atIndex:index]; + return YES; + } return NO; } @end diff --git a/MacPassTests/KPKTextPlaceholder.h b/MacPassTests/KPKTestPlaceholder.h similarity index 100% rename from MacPassTests/KPKTextPlaceholder.h rename to MacPassTests/KPKTestPlaceholder.h diff --git a/MacPassTests/KPKTextPlaceholder.m b/MacPassTests/KPKTestPlaceholder.m similarity index 89% rename from MacPassTests/KPKTextPlaceholder.m rename to MacPassTests/KPKTestPlaceholder.m index 0f234a64..bd7bc1d4 100644 --- a/MacPassTests/KPKTextPlaceholder.m +++ b/MacPassTests/KPKTestPlaceholder.m @@ -6,11 +6,11 @@ // Copyright (c) 2013 HicknHack Software GmbH. All rights reserved. // -#import "KPKTextPlaceholder.h" +#import "KPKTestPlaceholder.h" #import "KPKEntry.h" #import "KPKAttribute.h" -#import "NSString+CommandString.h" +#import "NSString+Placeholder.h" @implementation KPKTextPlaceholder @@ -25,7 +25,8 @@ [entry addCustomAttribute:attribute]; NSString *placeholder = @"{USERNAME}{PASSWORD}{NOTHING}{URL}{S:extended}"; - NSString *evaluated = [placeholder evaluatePlaceholderWithEntry:entry]; + BOOL replaced; + NSString *evaluated = [placeholder evaluatePlaceholderWithEntry:entry didReplace:&replaced]; NSString *evaluatedGoal = [NSString stringWithFormat:@"%@%@{NOTHING}%@%@", entry.username, entry.password, entry.url, attribute.value]; STAssertTrue([evaluated isEqualToString:evaluatedGoal], @"Evaluated string must match"); }