diff --git a/MacPass.xcodeproj/project.pbxproj b/MacPass.xcodeproj/project.pbxproj index c97c8244..e4a3531c 100644 --- a/MacPass.xcodeproj/project.pbxproj +++ b/MacPass.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 4C0728BD17B5B7F7005A7DD9 /* MPPasswordEditWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C0728BC17B5B7F7005A7DD9 /* MPPasswordEditWindowController.m */; }; 4C0728BF17B68ED0005A7DD9 /* SavePanelAccessoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C0728BE17B68ED0005A7DD9 /* SavePanelAccessoryView.xib */; }; 4C08C3AE17B3022400BBBC95 /* KPKLegacyHeaderWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C08C3AD17B3022400BBBC95 /* KPKLegacyHeaderWriter.m */; }; + 4C0AF62F195C1F2B009E658D /* MPEntrySearchContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C0AF62E195C1F2B009E658D /* MPEntrySearchContext.m */; }; 4C0B038C18E36DA400B9F9C9 /* MPFixAutotypeWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C0B038A18E36DA400B9F9C9 /* MPFixAutotypeWindowController.m */; }; 4C0B038D18E36DA400B9F9C9 /* FixAutotypeWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C0B038B18E36DA400B9F9C9 /* FixAutotypeWindow.xib */; }; 4C0C59F118B17F10009C7B76 /* DDHotKeyUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C0C59EF18B17F10009C7B76 /* DDHotKeyUtilities.m */; }; @@ -384,6 +385,8 @@ 4C08C3AC17B3022400BBBC95 /* KPKLegacyHeaderWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KPKLegacyHeaderWriter.h; sourceTree = ""; }; 4C08C3AD17B3022400BBBC95 /* KPKLegacyHeaderWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KPKLegacyHeaderWriter.m; sourceTree = ""; }; 4C08C3AF17B3036500BBBC95 /* KPKLegacyFormat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = KPKLegacyFormat.h; path = Format/KPKLegacyFormat.h; sourceTree = ""; }; + 4C0AF62D195C1F2B009E658D /* MPEntrySearchContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntrySearchContext.h; sourceTree = ""; }; + 4C0AF62E195C1F2B009E658D /* MPEntrySearchContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntrySearchContext.m; sourceTree = ""; }; 4C0B038918E36DA400B9F9C9 /* MPFixAutotypeWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixAutotypeWindowController.h; sourceTree = ""; }; 4C0B038A18E36DA400B9F9C9 /* MPFixAutotypeWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFixAutotypeWindowController.m; sourceTree = ""; }; 4C0B038B18E36DA400B9F9C9 /* FixAutotypeWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FixAutotypeWindow.xib; sourceTree = ""; }; @@ -1366,6 +1369,8 @@ 4C94A0711938DDC20040ABAB /* MPDocument+EditingSession.m */, 4C94A06D1938DC8C0040ABAB /* MPEditSession.h */, 4C94A06E1938DC8C0040ABAB /* MPEditSession.m */, + 4C0AF62D195C1F2B009E658D /* MPEntrySearchContext.h */, + 4C0AF62E195C1F2B009E658D /* MPEntrySearchContext.m */, ); name = Model; sourceTree = ""; @@ -2304,6 +2309,7 @@ 4C8EB8C118D399FD00438B08 /* KPKTag.m in Sources */, 4C94A06F1938DC8C0040ABAB /* MPEditSession.m in Sources */, 4CFC53BF16E94729007396BE /* MPShadowBox.m in Sources */, + 4C0AF62F195C1F2B009E658D /* MPEntrySearchContext.m in Sources */, 4C888C9316EB6F5E003D34A1 /* MPToolbarItem.m in Sources */, 4C888C9716EB754B003D34A1 /* MPActionHelper.m in Sources */, 4C811C8316ECD06E00C4BAC6 /* MPKeyfilePathControlDelegate.m in Sources */, diff --git a/MacPass/ContextBar.xib b/MacPass/ContextBar.xib index 04dffb30..5621f982 100644 --- a/MacPass/ContextBar.xib +++ b/MacPass/ContextBar.xib @@ -7,7 +7,6 @@ - @@ -15,6 +14,7 @@ + @@ -102,31 +102,38 @@ - + + + + + + + + + - + + - + - @@ -135,6 +142,8 @@ + + diff --git a/MacPass/MPContextBarViewController.h b/MacPass/MPContextBarViewController.h index 4eeadb6a..0ed45c0d 100644 --- a/MacPass/MPContextBarViewController.h +++ b/MacPass/MPContextBarViewController.h @@ -20,6 +20,7 @@ @property (weak) IBOutlet NSButton *urlButton; @property (weak) IBOutlet NSButton *notesButton; @property (weak) IBOutlet NSButton *duplicatePasswordsButton; +@property (weak) IBOutlet NSPopUpButton *specialFilterPopUpButton; - (void)registerNotificationsForDocument:(MPDocument *)document; diff --git a/MacPass/MPContextBarViewController.m b/MacPass/MPContextBarViewController.m index fe08badd..66a1163f 100644 --- a/MacPass/MPContextBarViewController.m +++ b/MacPass/MPContextBarViewController.m @@ -7,13 +7,15 @@ // #import "MPContextBarViewController.h" -#import "HNHGradientView.h" #import "KPKEntry.h" + #import "MPDocument+HistoryBrowsing.h" #import "MPDocument+Search.h" +#import "MPFlagsHelper.h" +#import "MPEntrySearchContext.h" #import "NSButton+HNHTextColor.h" -#import "MPFlagsHelper.h" +#import "HNHGradientView.h" #import "HNHCommon.h" NSUInteger const MPContextBarViewControllerActiveFilterMenuItemTag = 1000; @@ -68,12 +70,24 @@ typedef NS_ENUM(NSUInteger, MPContextTab) { self.emptyTrashButton.textColor = [NSColor whiteColor]; - NSInteger tags[] = { MPEntrySearchTitles, MPEntrySearchUsernames, MPEntrySearchPasswords, MPEntrySearchNotes, MPEntrySearchUrls, MPEntrySearchDoublePasswords }; - NSArray *buttons = @[self.titleButton, self.usernameButton, self.passwordButton, self.notesButton, self.urlButton, self.duplicatePasswordsButton ]; + NSInteger tags[] = { MPEntrySearchTitles, MPEntrySearchUsernames, MPEntrySearchPasswords, MPEntrySearchNotes, MPEntrySearchUrls }; + NSArray *buttons = @[self.titleButton, self.usernameButton, self.passwordButton, self.notesButton, self.urlButton ]; for(NSUInteger iIndex = 0; iIndex < [buttons count]; iIndex++) { [buttons[iIndex] setAction:@selector(toggleSearchFlags:)]; [buttons[iIndex] setTag:tags[iIndex]]; } + NSInteger specialTags[] = { MPEntrySearchDoublePasswords, MPEntrySearchExpiredEntries }; + NSArray *titles = @[ NSLocalizedString(@"SEARCH_DUPLICATE_PASSWORDS", ""), NSLocalizedString(@"SEARCH_EXPIRED_ENTRIES", "") ]; + NSMenu *specialMenu = [[NSMenu alloc] initWithTitle:@"Special Filters Menu"]; + [specialMenu addItemWithTitle:NSLocalizedString(@"SELECT_FILTER_WITH_DOTS", "") action:NULL keyEquivalent:@""]; + [[specialMenu itemAtIndex:0] setEnabled:NO]; + [[specialMenu itemAtIndex:0] setTag:MPEntrySearchNone]; + for(NSInteger iIndex = 0; iIndex < [titles count]; iIndex++) { + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:titles[iIndex] action:@selector(toggleSearchFlags:) keyEquivalent:@""]; + [item setTag:specialTags[iIndex]]; + [specialMenu addItem:item]; + } + [self.specialFilterPopUpButton setMenu:specialMenu]; [self _updateFilterButtons]; } @@ -122,12 +136,29 @@ typedef NS_ENUM(NSUInteger, MPContextTab) { - (void)_updateFilterButtons { MPDocument *document = [[self windowController] document]; - [self.duplicatePasswordsButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchDoublePasswords, document.activeFlags))]; - [self.notesButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchNotes, document.activeFlags))]; - [self.passwordButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchPasswords, document.activeFlags))]; - [self.titleButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchTitles, document.activeFlags))]; - [self.urlButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchUrls, document.activeFlags))]; - [self.usernameButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchUsernames, document.activeFlags))]; + MPEntrySearchFlags currentFlags = document.searchContext.searchFlags; + [self.duplicatePasswordsButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchDoublePasswords, currentFlags))]; + [self.notesButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchNotes, currentFlags))]; + [self.passwordButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchPasswords, currentFlags))]; + [self.titleButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchTitles, currentFlags))]; + [self.urlButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchUrls, currentFlags))]; + [self.usernameButton setState:HNHStateForBool(MPTestFlagInOptions(MPEntrySearchUsernames, currentFlags))]; + NSInteger selectedTag = MPEntrySearchNone; + for(NSMenuItem *item in [[self.specialFilterPopUpButton menu] itemArray]) { + MPEntrySearchFlags flag = [item tag]; + if(flag == MPEntrySearchNone) { + [item setState:NSOffState]; + [item setEnabled:NO]; + } + else { + BOOL isActive = MPTestFlagInOptions(flag, currentFlags); + if(isActive) { + selectedTag = flag; + } + [item setState:HNHStateForBool(isActive)]; + } + } + [self.specialFilterPopUpButton selectItemWithTag:selectedTag]; } @end diff --git a/MacPass/MPDocument+Search.h b/MacPass/MPDocument+Search.h index c5a76cf1..ca211af2 100644 --- a/MacPass/MPDocument+Search.h +++ b/MacPass/MPDocument+Search.h @@ -8,6 +8,8 @@ #import "MPDocument.h" +@class MPEntrySearchContext; + FOUNDATION_EXTERN NSString *const MPDocumentDidEnterSearchNotification; FOUNDATION_EXTERN NSString *const MPDocumentDidChangeSearchFlags; FOUNDATION_EXTERN NSString *const MPDocumentDidExitSearchNotification; @@ -23,6 +25,8 @@ FOUNDATION_EXTERN NSString *const kMPDocumentSearchResultsKey; @interface MPDocument (Search) +- (void)enterSearchWithContext:(MPEntrySearchContext *)context; + /* 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 cb9460ee..288016dc 100644 --- a/MacPass/MPDocument+Search.m +++ b/MacPass/MPDocument+Search.m @@ -12,6 +12,7 @@ #import "KPKGroup.h" #import "KPKEntry.h" +#import "KPKTimeInfo.h" #import "MPFlagsHelper.h" @@ -26,21 +27,30 @@ NSString *const kMPDocumentSearchResultsKey = @"kMPDocumentSearchResul @implementation MPDocument (Search) -#pragma mark Actions -- (void)performFindPanelAction:(id)sender { - self.hasSearch = YES; +- (void)enterSearchWithContext:(MPEntrySearchContext *)context { + /* the search context is loaded via defaults */ + self.searchContext = context; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateSearch:) name:NSUndoManagerDidRedoChangeNotification object:self.undoManager]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateSearch:) name:NSUndoManagerDidUndoChangeNotification object:self.undoManager]; [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidEnterSearchNotification object:self]; [self updateSearch:self]; } +#pragma mark Actions + +- (void)performFindPanelAction:(id)sender { + [self enterSearchWithContext:[MPEntrySearchContext userContext]]; +} + - (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 enterSearchWithContext:[MPEntrySearchContext userContext]]; + return; // We get called back! } - self.hasSearch = YES; + MPDocumentWindowController *windowController = [self windowControllers][0]; + NSString *searchString = [windowController.searchField stringValue]; + self.searchContext.searchString = searchString; MPDocument __weak *weakSelf = self; dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(backgroundQueue, ^{ @@ -53,8 +63,9 @@ NSString *const kMPDocumentSearchResultsKey = @"kMPDocumentSearchResul } - (void)exitSearch:(id)sender { - self.searchString = nil; - self.hasSearch = NO; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSUndoManagerDidUndoChangeNotification object:self.undoManager]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSUndoManagerDidRedoChangeNotification object:self.undoManager]; + self.searchContext = nil; [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidExitSearchNotification object:self]; } @@ -69,38 +80,43 @@ NSString *const kMPDocumentSearchResultsKey = @"kMPDocumentSearchResul MPEntrySearchFlags toggleFlag = [sender tag]; MPEntrySearchFlags newFlags = MPEntrySearchNone; BOOL isDoublePasswordFlag = (toggleFlag == MPEntrySearchDoublePasswords); + BOOL isExpiredFlag = (toggleFlag == MPEntrySearchExpiredEntries); NSButton *button = sender; switch([button state]) { case NSOffState: toggleFlag ^= MPEntrySearchAllFlags; - newFlags = isDoublePasswordFlag ? oldFlags : (self.activeFlags & toggleFlag); + newFlags = isDoublePasswordFlag ? oldFlags : (self.searchContext.searchFlags & toggleFlag); break; case NSOnState: - if(isDoublePasswordFlag) { - oldFlags = self.activeFlags; - newFlags = MPEntrySearchDoublePasswords; + if(isDoublePasswordFlag || isExpiredFlag ) { + oldFlags = self.searchContext.searchFlags; + newFlags = toggleFlag;//MPEntrySearchDoublePasswords; } else { /* always mask the double passwords in case another button was pressed */ - self.activeFlags &= (MPEntrySearchDoublePasswords ^ MPEntrySearchAllFlags); - newFlags = self.activeFlags | toggleFlag; + self.searchContext.searchFlags &= ((MPEntrySearchDoublePasswords | MPEntrySearchExpiredEntries) ^ MPEntrySearchAllFlags); + newFlags = self.searchContext.searchFlags | toggleFlag; } break; default: NSAssert(NO, @"Internal state is inconsistent"); return; } - if(newFlags != self.activeFlags) { - self.activeFlags = (newFlags == MPEntrySearchNone) ? MPEntrySearchTitles : newFlags; + if(newFlags != self.searchContext.searchFlags) { + self.searchContext.searchFlags = (newFlags == MPEntrySearchNone) ? MPEntrySearchTitles : newFlags; [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidChangeSearchFlags object:self]; [self updateSearch:self]; } } +- (NSArray *)entriesMatchingSearch:(MPEntrySearchContext *)search { + return nil; +} + #pragma mark Search - (NSArray *)_findEntriesMatchingCurrentSearch { /* Filter double passwords */ - if(MPTestFlagInOptions(MPEntrySearchDoublePasswords, self.activeFlags)) { + if(MPTestFlagInOptions(MPEntrySearchDoublePasswords, self.searchContext.searchFlags)) { __block NSMutableDictionary *passwordToEntryMap = [[NSMutableDictionary alloc] initWithCapacity:100]; /* Build up a usage map */ [[self.root childEntries] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { @@ -126,8 +142,15 @@ NSString *const kMPDocumentSearchResultsKey = @"kMPDocumentSearchResul }]; return doublePasswords; } + if(MPTestFlagInOptions(MPEntrySearchExpiredEntries, self.searchContext.searchFlags)) { + NSPredicate *expiredPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { + KPKNode *node = evaluatedObject; + return node.timeInfo.isExpired; + }]; + return [[self.root childEntries] filteredArrayUsingPredicate:expiredPredicate]; + } /* Filter using predicates */ - NSArray *predicates = [self _filterPredicatesWithString:self.searchString]; + NSArray *predicates = [self _filterPredicatesWithString:self.searchContext.searchString]; if(predicates) { NSPredicate *fullFilter = [NSCompoundPredicate orPredicateWithSubpredicates:predicates]; return [[self.root childEntries] filteredArrayUsingPredicate:fullFilter]; @@ -136,34 +159,22 @@ NSString *const kMPDocumentSearchResultsKey = @"kMPDocumentSearchResul return [self.root childEntries]; } -- (NSArray *)optionsEnabledInMode:(MPEntrySearchFlags)mode { - NSArray *allOptions = @[ @(MPEntrySearchUrls), @(MPEntrySearchUsernames), - @(MPEntrySearchTitles), @(MPEntrySearchPasswords) , - @(MPEntrySearchNotes), @(MPEntrySearchDoublePasswords) ]; - - NSIndexSet *indexes = [allOptions indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { - MPEntrySearchFlags flag = [obj integerValue]; - return MPTestFlagInOptions(flag, mode); - }]; - return [allOptions objectsAtIndexes:indexes]; -} - - (NSArray *)_filterPredicatesWithString:(NSString *)string{ NSMutableArray *prediactes = [[NSMutableArray alloc] initWithCapacity:4]; - if(MPTestFlagInOptions(MPEntrySearchTitles, self.activeFlags)) { + if(MPTestFlagInOptions(MPEntrySearchTitles, self.searchContext.searchFlags)) { [prediactes addObject:[NSPredicate predicateWithFormat:@"SELF.title CONTAINS[cd] %@", string]]; } - if(MPTestFlagInOptions(MPEntrySearchUsernames, self.activeFlags)) { + if(MPTestFlagInOptions(MPEntrySearchUsernames, self.searchContext.searchFlags)) { [prediactes addObject:[NSPredicate predicateWithFormat:@"SELF.username CONTAINS[cd] %@", string]]; } - if(MPTestFlagInOptions(MPEntrySearchUrls, self.activeFlags)) { + if(MPTestFlagInOptions(MPEntrySearchUrls, self.searchContext.searchFlags)) { [prediactes addObject:[NSPredicate predicateWithFormat:@"SELF.url CONTAINS[cd] %@", string]]; } - if(MPTestFlagInOptions(MPEntrySearchPasswords, self.activeFlags)) { + if(MPTestFlagInOptions(MPEntrySearchPasswords, self.searchContext.searchFlags)) { [prediactes addObject:[NSPredicate predicateWithFormat:@"SELF.password CONTAINS[cd] %@", string]]; } - if(MPTestFlagInOptions(MPEntrySearchNotes, self.activeFlags)) { + if(MPTestFlagInOptions(MPEntrySearchNotes, self.searchContext.searchFlags)) { [prediactes addObject:[NSPredicate predicateWithFormat:@"SELF.notes CONTAINS[cd] %@", string]]; } return prediactes; diff --git a/MacPass/MPDocument.h b/MacPass/MPDocument.h index ad256add..26a66bcd 100644 --- a/MacPass/MPDocument.h +++ b/MacPass/MPDocument.h @@ -22,6 +22,7 @@ #import #import "KPKVersion.h" +#import "MPEntrySearchContext.h" /** * Posted when a new group was added to the document. @@ -58,26 +59,9 @@ APPKIT_EXTERN NSString *const MPDocumentGroupKey; @class KPKNode; @class MPEditSession; -typedef NS_OPTIONS(NSUInteger, MPEntrySearchFlags) { - MPEntrySearchNone = 0, - MPEntrySearchUrls = (1<<0), - MPEntrySearchUsernames = (1<<1), - MPEntrySearchTitles = (1<<2), - MPEntrySearchPasswords = (1<<3), - MPEntrySearchNotes = (1<<4), - MPEntrySearchAllAttributes = (1<<5), - MPEntrySearchDoublePasswords = (1<<6), // Unused in GUI for now - MPEntrySearchAllFlags = (MPEntrySearchDoublePasswords | - MPEntrySearchNotes | - MPEntrySearchPasswords | - MPEntrySearchTitles | - MPEntrySearchUrls | - MPEntrySearchUsernames | - MPEntrySearchAllAttributes ) -}; - @interface MPDocument : NSDocument + @property (nonatomic, readonly, assign) BOOL encrypted; @property (nonatomic, readonly, assign) NSUInteger unlockCount; // Amount of times the Document was unlocked; @@ -102,9 +86,8 @@ typedef NS_OPTIONS(NSUInteger, MPEntrySearchFlags) { /* Search - see MPDocument+Search for further details */ -@property (nonatomic, assign) MPEntrySearchFlags activeFlags; -@property (nonatomic, copy) NSString *searchString; -@property (nonatomic, assign) BOOL hasSearch; +@property (nonatomic, readonly) BOOL hasSearch; +@property (nonatomic, copy) MPEntrySearchContext *searchContext; @property (nonatomic, strong) NSArray *searchResult; /* diff --git a/MacPass/MPDocument.m b/MacPass/MPDocument.m index fdd496a5..a0e0106b 100644 --- a/MacPass/MPDocument.m +++ b/MacPass/MPDocument.m @@ -123,9 +123,6 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey _encryptedData = nil; _didLockFile = NO; _readOnly = NO; - _activeFlags = MPEntrySearchTitles; - _hasSearch = NO; - _unlockCount = 0; self.tree = [KPKTree templateTree]; self.tree.metaData.rounds = [[NSUserDefaults standardUserDefaults] integerForKey:kMPSettingsKeyDefaultPasswordRounds]; } @@ -357,6 +354,10 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey return [self findGroup:self.tree.metaData.entryTemplatesGroup]; } +- (BOOL)hasSearch { + return self.searchContext != nil; +} + - (void)setTrash:(KPKGroup *)trash { if(self.useTrash) { if(![self.tree.metaData.recycleBinUuid isEqual:trash.uuid]) { diff --git a/MacPass/MPEntrySearchContext.h b/MacPass/MPEntrySearchContext.h new file mode 100644 index 00000000..370d9a8d --- /dev/null +++ b/MacPass/MPEntrySearchContext.h @@ -0,0 +1,56 @@ +// +// MPEntrySearch.h +// MacPass +// +// Created by Michael Starke on 26.06.14. +// Copyright (c) 2014 HicknHack Software GmbH. All rights reserved. +// + +#import + +typedef NS_OPTIONS(NSUInteger, MPEntrySearchFlags) { + MPEntrySearchNone = 0, + MPEntrySearchUrls = (1<<0), + MPEntrySearchUsernames = (1<<1), + MPEntrySearchTitles = (1<<2), + MPEntrySearchPasswords = (1<<3), + MPEntrySearchNotes = (1<<4), + MPEntrySearchAllAttributes = (1<<5), + /* The following two flags should be used like enums. + They are not intented to be used in conjunktion with any other flag */ + MPEntrySearchDoublePasswords = (1<<6), + MPEntrySearchExpiredEntries = (1<<7), + /* All search flags that are combineable combined */ + MPEntrySearchAllFlags = (MPEntrySearchDoublePasswords | + MPEntrySearchExpiredEntries | + MPEntrySearchNotes | + MPEntrySearchPasswords | + MPEntrySearchTitles | + MPEntrySearchUrls | + MPEntrySearchUsernames | + MPEntrySearchAllAttributes ) +}; + + +/* Wrap serach criteria to be able to store them */ +@interface MPEntrySearchContext : NSObject + +/** + * Returns a default search context initalized with sane values. + * + * @return The default search context + */ ++ (instancetype)defaultContext; +/** + * Returns the search context using the users preferences. If none are found, a default context is created + * + * @return Search context configured to the users data. If nothing is configures, defaultContext is used + */ ++ (instancetype)userContext; + +- (instancetype)initWithString:(NSString *)searchString flags:(MPEntrySearchFlags)flags; + +@property (nonatomic, assign) NSInteger searchFlags; +@property (nonatomic, copy) NSString *searchString; + +@end diff --git a/MacPass/MPEntrySearchContext.m b/MacPass/MPEntrySearchContext.m new file mode 100644 index 00000000..56a15fb0 --- /dev/null +++ b/MacPass/MPEntrySearchContext.m @@ -0,0 +1,82 @@ +// +// MPEntrySearch.m +// MacPass +// +// Created by Michael Starke on 26.06.14. +// Copyright (c) 2014 HicknHack Software GmbH. All rights reserved. +// + +#import "MPEntrySearchContext.h" +#import "MPSettingsHelper.h" + +@implementation MPEntrySearchContext + ++ (BOOL)supportsSecureCoding { + return YES; +} + ++ (instancetype)defaultContext { + return [[MPEntrySearchContext alloc] init]; +} + ++ (instancetype)userContext { + NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:kMPSettingsKeyEntrySearchFilterContext]; + if(data) { + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; + } + return [self defaultContext]; +} + +- (instancetype)init { + self = [self initWithString:nil flags:MPEntrySearchTitles|MPEntrySearchUsernames]; + return self; +} + +- (instancetype)initWithString:(NSString *)searchString flags:(MPEntrySearchFlags)flags { + self = [super init]; + if(self) { + self.searchFlags = flags; + self.searchString = searchString; + } + return self; + +} + +#pragma mark NSSecureCoding +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInteger:self.searchFlags forKey:NSStringFromSelector(@selector(searchFlags))]; + [aCoder encodeObject:self.searchString forKey:NSStringFromSelector(@selector(searchString))]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [self init]; + self.searchString = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(@selector(searchString))]; + self.searchFlags = [aDecoder decodeIntegerForKey:NSStringFromSelector(@selector(searchFlags))]; + return self; +} + +#pragma mark NSCopying +- (id)copyWithZone:(NSZone *)zone { + return [[MPEntrySearchContext alloc] initWithString:self.searchString flags:self.searchFlags]; +} + +- (void)setSearchFlags:(NSInteger)searchFlags { + if(_searchFlags != searchFlags) { + _searchFlags = searchFlags; + [self _updatePreferences]; + } +} + +- (void)setSearchString:(NSString *)searchString { + if(![_searchString isEqualToString:searchString]) { + _searchString = [searchString copy]; + [self _updatePreferences]; + } +} + +- (void )_updatePreferences { + NSData *myData = [NSKeyedArchiver archivedDataWithRootObject:self]; + [[NSUserDefaults standardUserDefaults] setObject:myData forKey:kMPSettingsKeyEntrySearchFilterContext]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} +@end diff --git a/MacPass/MPSettingsHelper.h b/MacPass/MPSettingsHelper.h index a87014c9..ffb0acd8 100644 --- a/MacPass/MPSettingsHelper.h +++ b/MacPass/MPSettingsHelper.h @@ -51,7 +51,7 @@ APPKIT_EXTERN NSString *const kMPSettingsKeyEnableGlobalAutotype; // APPKIT_EXTERN NSString *const kMPSettingsKeyGlobalAutotypeKeyDataKey; // The stored Data for the useder defined global autotype key /* Search */ -APPKIT_EXTERN NSString *const kMPSettingsKeyEntrySearchFilterMode; +APPKIT_EXTERN NSString *const kMPSettingsKeyEntrySearchFilterContext; /* Quicklook */ APPKIT_EXTERN NSString *const kMPSettingsKeyEnableQuicklookPreview; diff --git a/MacPass/MPSettingsHelper.m b/MacPass/MPSettingsHelper.m index 0c54315b..1e35260e 100644 --- a/MacPass/MPSettingsHelper.m +++ b/MacPass/MPSettingsHelper.m @@ -37,7 +37,7 @@ NSString *const kMPSettingsKeySendCommandForControlKey = @"SendCo NSString *const kMPSettingsKeyEnableGlobalAutotype = @"EnableGlobalAutotype"; NSString *const kMPSettingsKeyGlobalAutotypeKeyDataKey = @"GlobalAutotypeKeyDataKey"; -NSString *const kMPSettingsKeyEntrySearchFilterMode = @"EntrySearchFilterMode"; +NSString *const kMPSettingsKeyEntrySearchFilterContext = @"EntrySearchFilterContext"; NSString *const kMPSettingsKeyEnableQuicklookPreview = @"EnableQuicklookPreview"; @@ -57,6 +57,7 @@ NSString *const kMPDeprecatedSettingsKeyRememberKeyFilesForDatabases = @"kM NSString *const kMPDeprecatedSettingsKeyLastDatabasePath = @"MPLastDatabasePath"; NSString *const kMPDeprecatedSettingsKeyDocumentsAutotypeFixNoteWasShown = @"DocumentsAutotypeFixNoteWasShown"; NSString *const kMPDeprecatedSettingsKeyDoubleClickURLToLaunch = @"DoubleClickURLToLaunch"; +NSString *const kMPDeprecatedSettingsKeyEntrySearchFilterMode = @"EntrySearchFilterMode"; @implementation MPSettingsHelper @@ -67,6 +68,7 @@ NSString *const kMPDeprecatedSettingsKeyDoubleClickURLToLaunch = @"Do + (void)migrateDefaults { [self _fixEntryTableSortDescriptors]; [self _migrateURLDoubleClickPreferences]; + [self _migrateEntrySearchFlags]; [self _removeDeprecatedValues]; } @@ -96,7 +98,6 @@ NSString *const kMPDeprecatedSettingsKeyDoubleClickURLToLaunch = @"Do kMPSettingsKeyLegacyHideUsername: @NO, kMPSettingsKeyRememberKeyFilesForDatabases: @NO, kMPSettingsKeySendCommandForControlKey: @YES, - kMPSettingsKeyEntrySearchFilterMode: @0, kMPSettingsKeyEnableGlobalAutotype: @NO, kMPSettingsKeyEnableQuicklookPreview: @NO, kMPSettingsKeyCopyGeneratedPasswordToClipboard: @NO, @@ -119,7 +120,8 @@ NSString *const kMPDeprecatedSettingsKeyDoubleClickURLToLaunch = @"Do deprecatedSettings = @[ kMPDeprecatedSettingsKeyRememberKeyFilesForDatabases, kMPDeprecatedSettingsKeyLastDatabasePath, kMPDeprecatedSettingsKeyDocumentsAutotypeFixNoteWasShown, - kMPDeprecatedSettingsKeyDoubleClickURLToLaunch ]; + kMPDeprecatedSettingsKeyDoubleClickURLToLaunch, + kMPDeprecatedSettingsKeyEntrySearchFilterMode]; }); return deprecatedSettings; } @@ -159,4 +161,13 @@ NSString *const kMPDeprecatedSettingsKeyDoubleClickURLToLaunch = @"Do } } ++ (void)_migrateEntrySearchFlags { + NSInteger flags = [[NSUserDefaults standardUserDefaults] integerForKey:kMPDeprecatedSettingsKeyEntrySearchFilterMode]; + if(flags != 0) { + MPEntrySearchContext *context = [[MPEntrySearchContext alloc] initWithString:nil flags:flags]; + NSData *contextData = [NSKeyedArchiver archivedDataWithRootObject:context]; + [[NSUserDefaults standardUserDefaults] setObject:contextData forKey:kMPSettingsKeyEntrySearchFilterContext]; + } +} + @end diff --git a/MacPass/OutlineView.xib b/MacPass/OutlineView.xib index 1de84ec2..1ef91d5a 100644 --- a/MacPass/OutlineView.xib +++ b/MacPass/OutlineView.xib @@ -1,8 +1,8 @@ - + - + diff --git a/MacPass/en.lproj/Localizable.strings b/MacPass/en.lproj/Localizable.strings index 6ee18d0d..6d7f7e5d 100644 Binary files a/MacPass/en.lproj/Localizable.strings and b/MacPass/en.lproj/Localizable.strings differ