diff --git a/MacPass/MPDocument+Search.h b/MacPass/MPDocument+Search.h index 69e244e3..c5a76cf1 100644 --- a/MacPass/MPDocument+Search.h +++ b/MacPass/MPDocument+Search.h @@ -8,16 +8,21 @@ #import "MPDocument.h" -FOUNDATION_EXPORT NSString *const MPDocumentDidEnterSearchNotification; -FOUNDATION_EXTERN NSString *const MPDocumentDidChangeSearchNotification; -FOUNDATION_EXPORT NSString *const MPDocumentDidChangeSearchFlags; +FOUNDATION_EXTERN NSString *const MPDocumentDidEnterSearchNotification; +FOUNDATION_EXTERN NSString *const MPDocumentDidChangeSearchFlags; FOUNDATION_EXTERN NSString *const MPDocumentDidExitSearchNotification; -FOUNDATION_EXPORT NSString *const MPDocumentDidChangeSearchResults; +/** + * Posted by the document, when the search results have been updated. This is only called when searching. + * If the search is exited, it will be notified by MPDocumentDidExitSearchNotification + * The userInfo dictionary has one key kMPDocumentSearchResultsKey with an NSArray of KPKEntries mathching the search. + */ +FOUNDATION_EXTERN NSString *const MPDocumentDidChangeSearchResults; + +/* keys used in userInfo dictionaries on notifications */ +FOUNDATION_EXTERN NSString *const kMPDocumentSearchResultsKey; @interface MPDocument (Search) -- (NSArray *)entriesInDocument:(MPDocument *)document matching:(NSString *)string; - /* Should be called by the NSSearchTextField to update the search string */ - (IBAction)updateSearch:(id)sender; /* exits searching mode */ diff --git a/MacPass/MPDocument+Search.m b/MacPass/MPDocument+Search.m index 35b1f716..ba0da74d 100644 --- a/MacPass/MPDocument+Search.m +++ b/MacPass/MPDocument+Search.m @@ -8,32 +8,47 @@ #import "MPDocument+Search.h" #import "MPDocument.h" +#import "MPDocumentWindowController.h" + #import "KPKGroup.h" #import "KPKEntry.h" + #import "MPFlagsHelper.h" NSString *const MPDocumentDidEnterSearchNotification = @"com.hicknhack.macpass.MPDocumentDidEnterSearchNotification"; -NSString *const MPDocumentDidChangeSearchNotification = @"com.hicknhack.macpass.MPDocumentDidChangeSearchNotification"; NSString *const MPDocumentDidChangeSearchFlags = @"com.hicknhack.macpass.MPDocumentDidChangeSearchFlagsNotification"; NSString *const MPDocumentDidExitSearchNotification = @"com.hicknhack.macpass.MPDocumentDidExitSearchNotification"; +NSString *const MPDocumentDidChangeSearchResults = @"com.hicknhack.macpass.MPDocumentDidChangeSearchResults"; + +NSString *const kMPDocumentSearchResultsKey = @"kMPDocumentSearchResultsKey"; + + @implementation MPDocument (Search) #pragma mark Actions - (void)performFindPanelAction:(id)sender { self.hasSearch = YES; - NSWindow *window = [[self windowControllers][0] window]; - NSToolbar *toolbar = [window toolbar]; - if(![toolbar isVisible]) { - [toolbar setVisible:YES]; - } [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidEnterSearchNotification object:self]; } - (void)updateSearch:(id)sender { + MPDocumentWindowController *windowController = [self windowControllers][0]; + self.searchString = [windowController.searchField stringValue]; + if(NO == self.hasSearch) { + [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidEnterSearchNotification object:self]; + } self.hasSearch = YES; - [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidChangeSearchNotification object:self]; + MPDocument __weak *weakSelf = self; + dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_async(backgroundQueue, ^{ + NSArray *results = [weakSelf _findEntriesMatchingCurrentSearch]; + dispatch_sync(dispatch_get_main_queue(), ^{ + weakSelf.selectedEntry = nil; + [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidChangeSearchResults object:weakSelf userInfo:@{ kMPDocumentSearchResultsKey: results }]; + }); + }); } - (void)exitSearch:(id)sender { @@ -65,16 +80,18 @@ NSString *const MPDocumentDidExitSearchNotification = @"com.hicknhack.macpass. if(newFlags == self.activeFlags) { self.activeFlags = (newFlags == MPEntrySearchNone) ? MPEntrySearchTitles : newFlags; [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidChangeSearchFlags object:self]; + [self updateSearch:self]; } } #pragma mark Search -- (NSArray *)entriesInDocument:(MPDocument *)document matching:(NSString *)string { +- (NSArray *)_findEntriesMatchingCurrentSearch { /* Filter double passwords */ + MPDocument __weak *weakSelf = self; if(MPTestFlagInOptions(MPEntrySearchDoublePasswords, self.activeFlags)) { __block NSMutableDictionary *passwordToEntryMap; /* Build up a usage map */ - [[document.root childEntries] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [[weakSelf.root childEntries] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { KPKEntry *entry = obj; NSMutableSet *entrySet = passwordToEntryMap[entry.password]; if(entrySet) { @@ -96,13 +113,13 @@ NSString *const MPDocumentDidExitSearchNotification = @"com.hicknhack.macpass. return doublePasswords; } /* Filter using predicates */ - NSArray *predicates = [self _filterPredicatesWithString:string]; + NSArray *predicates = [self _filterPredicatesWithString:self.searchString]; if(predicates) { NSPredicate *fullFilter = [NSCompoundPredicate orPredicateWithSubpredicates:predicates]; - return [[document.root childEntries] filteredArrayUsingPredicate:fullFilter]; + return [[self.root childEntries] filteredArrayUsingPredicate:fullFilter]; } /* No filter, just return everything */ - return [document.root childEntries]; + return [self.root childEntries]; } - (NSArray *)optionsEnabledInMode:(MPEntrySearchFlags)mode { @@ -117,10 +134,6 @@ NSString *const MPDocumentDidExitSearchNotification = @"com.hicknhack.macpass. return [allOptions objectsAtIndexes:indexes]; } -- (void)_updateSearch { - self.searchResult = [self entriesInDocument:self matching:self.searchString]; -} - - (NSArray *)_filterPredicatesWithString:(NSString *)string{ NSMutableArray *prediactes = [[NSMutableArray alloc] initWithCapacity:4]; diff --git a/MacPass/MPDocumentWindowController.m b/MacPass/MPDocumentWindowController.m index e3466146..1fd279d1 100644 --- a/MacPass/MPDocumentWindowController.m +++ b/MacPass/MPDocumentWindowController.m @@ -33,6 +33,7 @@ typedef NS_ENUM(NSUInteger, MPAlertContext) { @private id _firstResponder; BOOL _saveAfterPasswordChange; + BOOL _didShowToolbarForSearch; } @property (strong) IBOutlet NSSplitView *splitView; @@ -57,6 +58,7 @@ typedef NS_ENUM(NSUInteger, MPAlertContext) { if( self ) { _firstResponder = nil; _saveAfterPasswordChange = NO; + _didShowToolbarForSearch = NO; _toolbarDelegate = [[MPToolbarDelegate alloc] init]; _outlineViewController = [[MPOutlineViewController alloc] init]; _entryViewController = [[MPEntryViewController alloc] init]; @@ -77,12 +79,13 @@ typedef NS_ENUM(NSUInteger, MPAlertContext) { [[self window] setDelegate:self.documentWindowDelegate]; [[self window] registerForDraggedTypes:@[NSURLPboardType]]; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didRevertDocument:) name:MPDocumentDidRevertNotifiation object:[self document]]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showEntries) name:MPDocumentDidUnlockDatabaseNotification object:[self document]]; - MPDocument *document = [self document]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didRevertDocument:) name:MPDocumentDidRevertNotifiation object:document]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showEntries) name:MPDocumentDidUnlockDatabaseNotification object:document]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didEnterSearch:) name:MPDocumentDidEnterSearchNotification object:document]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didExitSearch:) name:MPDocumentDidExitSearchNotification object:document]; + [_entryViewController regsiterNotificationsForDocument:document]; [_inspectorViewController regsiterNotificationsForDocument:document]; [_outlineViewController regsiterNotificationsForDocument:document]; @@ -112,7 +115,7 @@ typedef NS_ENUM(NSUInteger, MPAlertContext) { if(!showInspector) { [inspectorView removeFromSuperview]; } - + if(document.encrypted) { [self showPasswordInput]; } @@ -159,11 +162,32 @@ typedef NS_ENUM(NSUInteger, MPAlertContext) { [self.window makeFirstResponder:[viewController reconmendedFirstResponder]]; } +#pragma mark MPDocument notifications - (void)_didRevertDocument:(NSNotification *)notification { [self.outlineViewController clearSelection]; [self showPasswordInput]; } +- (void)_didEnterSearch:(NSNotificationCenter *)notification { + /* Hide again after search ? */ + NSWindow *window = [self window]; + NSToolbar *toolbar = [window toolbar]; + if(![toolbar isVisible]) { + _didShowToolbarForSearch = YES; + [toolbar setVisible:YES]; + } +} + +- (void)_didExitSearch:(NSNotification *)notification { + /* Hide again after search ? */ + NSWindow *window = [self window]; + NSToolbar *toolbar = [window toolbar]; + if(_didShowToolbarForSearch && [toolbar isVisible]) { + _didShowToolbarForSearch = NO; + [toolbar setVisible:NO]; + } +} + #pragma mark Actions - (void)saveDocument:(id)sender { _saveAfterPasswordChange = NO; @@ -359,7 +383,7 @@ typedef NS_ENUM(NSUInteger, MPAlertContext) { [_inspectorViewController updateResponderChain]; [_outlineViewController updateResponderChain]; [_outlineViewController showOutline]; - + /* Restore the State the inspector view was in before the view change */ if(removeInspector) { [inspectorView removeFromSuperview]; diff --git a/MacPass/MPEntryViewController.m b/MacPass/MPEntryViewController.m index c3c20536..ed928f67 100644 --- a/MacPass/MPEntryViewController.m +++ b/MacPass/MPEntryViewController.m @@ -195,21 +195,21 @@ NSString *const _MPTAbleSecurCellView = @"PasswordCell"; name:MPDocumentCurrentItemChangedNotification object:document]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_updateSearchResults:) - name:MPDocumentDidChangeSearchNotification - object:document]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didEnterSearch:) name:MPDocumentDidEnterSearchNotification object:document]; - - + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didExitSearch:) name:MPDocumentDidExitSearchNotification object:document]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_didUpdateSearchResults:) + name:MPDocumentDidChangeSearchResults + object:document]; + [self.contextBarViewController registerNotificationsForDocument:document]; } @@ -337,22 +337,15 @@ NSString *const _MPTAbleSecurCellView = @"PasswordCell"; } } #pragma mark MPDocumentSearchServiceNotifications -- (void)_updateSearchResults:(NSNotification *)notification { +- (void)_didUpdateSearchResults:(NSNotification *)notification { + BOOL main = [NSThread isMainThread]; [self _showContextBar]; - dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - dispatch_async(backgroundQueue, ^{ - MPDocument *document = [[self windowController] document]; - NSString *searchString = [[[self windowController] searchField] stringValue]; - self.filteredEntries = [document entriesInDocument:document - matching:searchString]; - - dispatch_sync(dispatch_get_main_queue(), ^{ - document.selectedEntry = nil; - [self.entryArrayController unbind:NSContentArrayBinding]; - [self.entryArrayController setContent:self.filteredEntries]; - [[self.entryTable tableColumnWithIdentifier:MPEntryTableParentColumnIdentifier] setHidden:NO]; - }); - }); + NSArray *result = [notification userInfo][kMPDocumentSearchResultsKey]; + NSAssert(result != nil, @"Resutls should never be nil"); + self.filteredEntries = result; + [self.entryArrayController unbind:NSContentArrayBinding]; + [self.entryArrayController setContent:self.filteredEntries]; + [[self.entryTable tableColumnWithIdentifier:MPEntryTableParentColumnIdentifier] setHidden:NO]; } #pragma mark NSDocument+Search Notifications