Added “everywhere” search option

This commit is contained in:
Michael Starke
2018-03-09 17:43:31 +01:00
parent 8fc55f06dc
commit 6f37b98d92
6 changed files with 55 additions and 25 deletions

View File

@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13196" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13196"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPContextBarViewController"> <customObject id="-2" userLabel="File's Owner" customClass="MPContextBarViewController">
<connections> <connections>
<outlet property="emptyTrashButton" destination="szx-Hx-OrV" id="i1Y-qB-TW3"/> <outlet property="emptyTrashButton" destination="szx-Hx-OrV" id="i1Y-qB-TW3"/>
<outlet property="everywhereButton" destination="RGg-wK-hz4" id="xIZ-YB-pkm"/>
<outlet property="exitHistoryButton" destination="pqx-su-vAh" id="JmV-vC-F48"/> <outlet property="exitHistoryButton" destination="pqx-su-vAh" id="JmV-vC-F48"/>
<outlet property="filterLabelTextField" destination="6" id="60"/> <outlet property="filterLabelTextField" destination="6" id="60"/>
<outlet property="historyBar" destination="S8L-rB-h0h" id="6yZ-El-fVs"/> <outlet property="historyBar" destination="S8L-rB-h0h" id="6yZ-El-fVs"/>
@@ -108,18 +109,27 @@
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="RGg-wK-hz4">
<rect key="frame" x="379" y="6" width="86" height="17"/>
<buttonCell key="cell" type="recessed" title="Everywhere" bezelStyle="recessed" alignment="center" controlSize="small" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WMK-bb-ESj">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
<font key="font" metaFont="systemBold" size="12"/>
</buttonCell>
</button>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstAttribute="centerY" secondItem="O7W-cn-eUP" secondAttribute="centerY" id="2uC-wS-HDi"/> <constraint firstAttribute="centerY" secondItem="O7W-cn-eUP" secondAttribute="centerY" id="2uC-wS-HDi"/>
<constraint firstItem="O7W-cn-eUP" firstAttribute="leading" secondItem="6" secondAttribute="trailing" constant="8" symbolic="YES" id="5hN-x3-XlX"/> <constraint firstItem="O7W-cn-eUP" firstAttribute="leading" secondItem="6" secondAttribute="trailing" constant="8" symbolic="YES" id="5hN-x3-XlX"/>
<constraint firstItem="0R1-PX-dgn" firstAttribute="leading" secondItem="O7W-cn-eUP" secondAttribute="trailing" constant="8" symbolic="YES" id="67f-i6-eOb"/> <constraint firstItem="0R1-PX-dgn" firstAttribute="leading" secondItem="O7W-cn-eUP" secondAttribute="trailing" constant="8" symbolic="YES" id="67f-i6-eOb"/>
<constraint firstAttribute="centerY" secondItem="SaV-5p-jIX" secondAttribute="centerY" id="7ch-eZ-Hzh"/> <constraint firstAttribute="centerY" secondItem="SaV-5p-jIX" secondAttribute="centerY" id="7ch-eZ-Hzh"/>
<constraint firstItem="aPQ-t2-bgz" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="SaV-5p-jIX" secondAttribute="trailing" constant="8" symbolic="YES" id="91Q-Aq-ulG"/> <constraint firstItem="RGg-wK-hz4" firstAttribute="leading" secondItem="SaV-5p-jIX" secondAttribute="trailing" constant="8" symbolic="YES" id="Bbh-au-wHa"/>
<constraint firstItem="aPQ-t2-bgz" firstAttribute="centerY" secondItem="SaV-5p-jIX" secondAttribute="centerY" id="EUG-hL-jbn"/> <constraint firstItem="aPQ-t2-bgz" firstAttribute="centerY" secondItem="SaV-5p-jIX" secondAttribute="centerY" id="EUG-hL-jbn"/>
<constraint firstAttribute="centerY" secondItem="0R1-PX-dgn" secondAttribute="centerY" id="Huh-PB-cbs"/> <constraint firstAttribute="centerY" secondItem="0R1-PX-dgn" secondAttribute="centerY" id="Huh-PB-cbs"/>
<constraint firstItem="aPQ-t2-bgz" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="RGg-wK-hz4" secondAttribute="trailing" constant="8" symbolic="YES" id="QoK-F3-goX"/>
<constraint firstItem="6" firstAttribute="leading" secondItem="1" secondAttribute="leading" constant="8" id="Seo-dI-FzX"/> <constraint firstItem="6" firstAttribute="leading" secondItem="1" secondAttribute="leading" constant="8" id="Seo-dI-FzX"/>
<constraint firstItem="Bn2-iY-mQ3" firstAttribute="leading" secondItem="Uhl-ck-vVQ" secondAttribute="trailing" constant="8" id="TCJ-vg-IIt"/> <constraint firstItem="Bn2-iY-mQ3" firstAttribute="leading" secondItem="Uhl-ck-vVQ" secondAttribute="trailing" constant="8" id="TCJ-vg-IIt"/>
<constraint firstAttribute="trailing" secondItem="aPQ-t2-bgz" secondAttribute="trailing" constant="20" symbolic="YES" id="XFv-Pv-Bzq"/> <constraint firstAttribute="trailing" secondItem="aPQ-t2-bgz" secondAttribute="trailing" constant="20" symbolic="YES" id="XFv-Pv-Bzq"/>
<constraint firstItem="RGg-wK-hz4" firstAttribute="centerY" secondItem="1" secondAttribute="centerY" id="hsT-uo-6H8"/>
<constraint firstItem="SaV-5p-jIX" firstAttribute="leading" secondItem="Bn2-iY-mQ3" secondAttribute="trailing" constant="8" symbolic="YES" id="pAA-uU-moF"/> <constraint firstItem="SaV-5p-jIX" firstAttribute="leading" secondItem="Bn2-iY-mQ3" secondAttribute="trailing" constant="8" symbolic="YES" id="pAA-uU-moF"/>
<constraint firstAttribute="centerY" secondItem="6" secondAttribute="centerY" id="qak-8F-xbj"/> <constraint firstAttribute="centerY" secondItem="6" secondAttribute="centerY" id="qak-8F-xbj"/>
<constraint firstItem="Uhl-ck-vVQ" firstAttribute="leading" secondItem="0R1-PX-dgn" secondAttribute="trailing" constant="8" id="u8d-C2-O9h"/> <constraint firstItem="Uhl-ck-vVQ" firstAttribute="leading" secondItem="0R1-PX-dgn" secondAttribute="trailing" constant="8" id="u8d-C2-O9h"/>
@@ -244,7 +254,7 @@
</connections> </connections>
</tabViewItem> </tabViewItem>
</tabViewItems> </tabViewItems>
<point key="canvasLocation" x="104" y="293"/> <point key="canvasLocation" x="-152" y="121"/>
</tabView> </tabView>
</objects> </objects>
</document> </document>

View File

@@ -35,6 +35,7 @@
@property (weak) IBOutlet NSButton *notesButton; @property (weak) IBOutlet NSButton *notesButton;
@property (weak) IBOutlet NSButton *duplicatePasswordsButton; @property (weak) IBOutlet NSButton *duplicatePasswordsButton;
@property (weak) IBOutlet NSPopUpButton *specialFilterPopUpButton; @property (weak) IBOutlet NSPopUpButton *specialFilterPopUpButton;
@property (weak) IBOutlet NSButton *everywhereButton;
- (void)registerNotificationsForDocument:(MPDocument *)document; - (void)registerNotificationsForDocument:(MPDocument *)document;

View File

@@ -85,8 +85,8 @@ typedef NS_ENUM(NSUInteger, MPContextTab) {
[self.view bind:NSSelectedIndexBinding toObject:self withKeyPath:NSStringFromSelector(@selector(activeTab)) options:nil]; [self.view bind:NSSelectedIndexBinding toObject:self withKeyPath:NSStringFromSelector(@selector(activeTab)) options:nil];
/* Setup Filter Bar buttons and menu */ /* Setup Filter Bar buttons and menu */
NSInteger tags[] = { MPEntrySearchTitles, MPEntrySearchUsernames, MPEntrySearchPasswords, MPEntrySearchNotes, MPEntrySearchUrls }; NSInteger tags[] = { MPEntrySearchTitles, MPEntrySearchUsernames, MPEntrySearchPasswords, MPEntrySearchNotes, MPEntrySearchUrls, MPEntrySearchAllAttributes };
NSArray<NSControl *> *buttons = @[self.titleButton, self.usernameButton, self.passwordButton, self.notesButton, self.urlButton ]; NSArray<NSControl *> *buttons = @[self.titleButton, self.usernameButton, self.passwordButton, self.notesButton, self.urlButton, self.everywhereButton ];
for(NSUInteger iIndex = 0; iIndex < buttons.count; iIndex++) { for(NSUInteger iIndex = 0; iIndex < buttons.count; iIndex++) {
buttons[iIndex].action = @selector(toggleSearchFlags:); buttons[iIndex].action = @selector(toggleSearchFlags:);
buttons[iIndex].tag = tags[iIndex]; buttons[iIndex].tag = tags[iIndex];
@@ -103,7 +103,7 @@ typedef NS_ENUM(NSUInteger, MPContextTab) {
item.tag = specialTags[iIndex]; item.tag = specialTags[iIndex];
[specialMenu addItem:item]; [specialMenu addItem:item];
} }
[self.specialFilterPopUpButton setMenu:specialMenu]; self.specialFilterPopUpButton.menu = specialMenu;
[self _updateFilterButtons]; [self _updateFilterButtons];
} }
@@ -154,8 +154,9 @@ typedef NS_ENUM(NSUInteger, MPContextTab) {
self.titleButton.state = HNHUIStateForBool(MPIsFlagSetInOptions(MPEntrySearchTitles, currentFlags)); self.titleButton.state = HNHUIStateForBool(MPIsFlagSetInOptions(MPEntrySearchTitles, currentFlags));
self.urlButton.state = HNHUIStateForBool(MPIsFlagSetInOptions(MPEntrySearchUrls, currentFlags)); self.urlButton.state = HNHUIStateForBool(MPIsFlagSetInOptions(MPEntrySearchUrls, currentFlags));
self.usernameButton.state = HNHUIStateForBool(MPIsFlagSetInOptions(MPEntrySearchUsernames, currentFlags)); self.usernameButton.state = HNHUIStateForBool(MPIsFlagSetInOptions(MPEntrySearchUsernames, currentFlags));
self.everywhereButton.state = HNHUIStateForBool(MPIsFlagSetInOptions(MPEntrySearchAllAttributes, currentFlags));
NSInteger selectedTag = MPEntrySearchNone; NSInteger selectedTag = MPEntrySearchNone;
for(NSMenuItem *item in [[self.specialFilterPopUpButton menu] itemArray]) { for(NSMenuItem *item in self.specialFilterPopUpButton.menu.itemArray) {
MPEntrySearchFlags flag = item.tag; MPEntrySearchFlags flag = item.tag;
if(flag == MPEntrySearchNone) { if(flag == MPEntrySearchNone) {
item.state = NSOffState; item.state = NSOffState;

View File

@@ -65,16 +65,16 @@ NSString *const kMPDocumentSearchResultsKey = @"kMPDocumentSearchResul
dispatch_async(backgroundQueue, ^{ dispatch_async(backgroundQueue, ^{
NSArray *results = [weakSelf _findEntriesMatchingCurrentSearch]; NSArray *results = [weakSelf _findEntriesMatchingCurrentSearch];
dispatch_sync(dispatch_get_main_queue(), ^{ dispatch_sync(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidChangeSearchResults object:weakSelf userInfo:@{ kMPDocumentSearchResultsKey: results }]; [NSNotificationCenter.defaultCenter postNotificationName:MPDocumentDidChangeSearchResults object:weakSelf userInfo:@{ kMPDocumentSearchResultsKey: results }];
}); });
}); });
} }
- (void)exitSearch:(id)sender { - (void)exitSearch:(id)sender {
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSUndoManagerDidUndoChangeNotification object:self.undoManager]; [NSNotificationCenter.defaultCenter removeObserver:self name:NSUndoManagerDidUndoChangeNotification object:self.undoManager];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSUndoManagerDidRedoChangeNotification object:self.undoManager]; [NSNotificationCenter.defaultCenter removeObserver:self name:NSUndoManagerDidRedoChangeNotification object:self.undoManager];
self.searchContext = nil; self.searchContext = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidExitSearchNotification object:self]; [NSNotificationCenter.defaultCenter postNotificationName:MPDocumentDidExitSearchNotification object:self];
} }
- (void)toggleSearchFlags:(id)sender { - (void)toggleSearchFlags:(id)sender {
@@ -88,7 +88,7 @@ NSString *const kMPDocumentSearchResultsKey = @"kMPDocumentSearchResul
MPEntrySearchFlags newFlags = MPEntrySearchNone; MPEntrySearchFlags newFlags = MPEntrySearchNone;
BOOL isSingleFlag = toggleFlag & MPEntrySearchSingleFlags; BOOL isSingleFlag = toggleFlag & MPEntrySearchSingleFlags;
NSButton *button = sender; NSButton *button = sender;
switch([button state]) { switch(button.state) {
case NSOffState: case NSOffState:
toggleFlag ^= MPEntrySearchAllCombineableFlags; toggleFlag ^= MPEntrySearchAllCombineableFlags;
newFlags = isSingleFlag ? MPEntrySearchNone : (self.searchContext.searchFlags & toggleFlag); newFlags = isSingleFlag ? MPEntrySearchNone : (self.searchContext.searchFlags & toggleFlag);
@@ -109,7 +109,7 @@ NSString *const kMPDocumentSearchResultsKey = @"kMPDocumentSearchResul
} }
if(newFlags != self.searchContext.searchFlags) { if(newFlags != self.searchContext.searchFlags) {
self.searchContext.searchFlags = (newFlags == MPEntrySearchNone) ? MPEntrySearchTitles : newFlags; self.searchContext.searchFlags = (newFlags == MPEntrySearchNone) ? MPEntrySearchTitles : newFlags;
[[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidChangeSearchFlags object:self]; [NSNotificationCenter.defaultCenter postNotificationName:MPDocumentDidChangeSearchFlags object:self];
[self updateSearch:self]; [self updateSearch:self];
} }
} }
@@ -166,6 +166,9 @@ NSString *const kMPDocumentSearchResultsKey = @"kMPDocumentSearchResul
- (NSArray *)_filterPredicatesWithString:(NSString *)string{ - (NSArray *)_filterPredicatesWithString:(NSString *)string{
NSMutableArray *prediactes = [[NSMutableArray alloc] initWithCapacity:4]; NSMutableArray *prediactes = [[NSMutableArray alloc] initWithCapacity:4];
BOOL searchInAllAttributes = MPIsFlagSetInOptions(MPEntrySearchAllAttributes, self.searchContext.searchFlags);
if(MPIsFlagSetInOptions(MPEntrySearchTitles, self.searchContext.searchFlags)) { if(MPIsFlagSetInOptions(MPEntrySearchTitles, self.searchContext.searchFlags)) {
[prediactes addObject:[NSPredicate predicateWithFormat:@"SELF.title CONTAINS[cd] %@", string]]; [prediactes addObject:[NSPredicate predicateWithFormat:@"SELF.title CONTAINS[cd] %@", string]];
} }
@@ -181,6 +184,23 @@ NSString *const kMPDocumentSearchResultsKey = @"kMPDocumentSearchResul
if(MPIsFlagSetInOptions(MPEntrySearchNotes, self.searchContext.searchFlags)) { if(MPIsFlagSetInOptions(MPEntrySearchNotes, self.searchContext.searchFlags)) {
[prediactes addObject:[NSPredicate predicateWithFormat:@"SELF.notes CONTAINS[cd] %@", string]]; [prediactes addObject:[NSPredicate predicateWithFormat:@"SELF.notes CONTAINS[cd] %@", string]];
} }
if(searchInAllAttributes) {
[prediactes addObject:[NSPredicate predicateWithFormat:@"SELF.tags CONTAINS[cd] %@", string]];
[prediactes addObject:[NSPredicate predicateWithFormat:@"SELF.uuid.UUIDString CONTAINS[cd] %@", string]];
NSPredicate *allAttributesPredicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
KPKEntry *entry = evaluatedObject;
for(KPKAttribute *attribute in entry.attributes) {
if([attribute.value rangeOfString:string options:NSCaseInsensitiveSearch].location != NSNotFound) {
return YES;
}
}
return NO;
}];
[prediactes addObject:allAttributesPredicate];
}
return prediactes; return prediactes;
} }

View File

@@ -30,10 +30,9 @@ typedef NS_OPTIONS(NSUInteger, MPEntrySearchFlags) {
MPEntrySearchPasswords = (1<<3), MPEntrySearchPasswords = (1<<3),
MPEntrySearchNotes = (1<<4), MPEntrySearchNotes = (1<<4),
MPEntrySearchAllAttributes = (1<<5), MPEntrySearchAllAttributes = (1<<5),
/* The following two flags should be used like enums. MPEntrySearchDoublePasswords = (1<<6), // do not combine with others. Exclusive flag
They are not intended to be used in conjunction with any other flag */ MPEntrySearchExpiredEntries = (1<<7), // do not combine with others. Exclusive flag
MPEntrySearchDoublePasswords = (1<<6),
MPEntrySearchExpiredEntries = (1<<7),
/* All combine-able search flags combined */ /* All combine-able search flags combined */
MPEntrySearchAllCombineableFlags = (MPEntrySearchDoublePasswords | MPEntrySearchAllCombineableFlags = (MPEntrySearchDoublePasswords |
MPEntrySearchExpiredEntries | MPEntrySearchExpiredEntries |
@@ -41,9 +40,8 @@ typedef NS_OPTIONS(NSUInteger, MPEntrySearchFlags) {
MPEntrySearchPasswords | MPEntrySearchPasswords |
MPEntrySearchTitles | MPEntrySearchTitles |
MPEntrySearchUrls | MPEntrySearchUrls |
MPEntrySearchUsernames | MPEntrySearchUsernames),
MPEntrySearchAllAttributes), MPEntrySearchSingleFlags = (MPEntrySearchDoublePasswords | MPEntrySearchExpiredEntries | MPEntrySearchAllAttributes ),
MPEntrySearchSingleFlags = (MPEntrySearchDoublePasswords | MPEntrySearchExpiredEntries),
MPEntrySearchAllFlags = (MPEntrySearchAllCombineableFlags | MPEntrySearchSingleFlags ) MPEntrySearchAllFlags = (MPEntrySearchAllCombineableFlags | MPEntrySearchSingleFlags )
}; };

View File

@@ -34,7 +34,7 @@
} }
+ (instancetype)userContext { + (instancetype)userContext {
NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:kMPSettingsKeyEntrySearchFilterContext]; NSData *data = [NSUserDefaults.standardUserDefaults dataForKey:kMPSettingsKeyEntrySearchFilterContext];
if(data) { if(data) {
return [NSKeyedUnarchiver unarchiveObjectWithData:data]; return [NSKeyedUnarchiver unarchiveObjectWithData:data];
} }
@@ -64,7 +64,7 @@
- (instancetype)initWithCoder:(NSCoder *)aDecoder { - (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [self init]; self = [self init];
self.searchString = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(@selector(searchString))]; self.searchString = [aDecoder decodeObjectOfClass:NSString.class forKey:NSStringFromSelector(@selector(searchString))];
self.searchFlags = [aDecoder decodeIntegerForKey:NSStringFromSelector(@selector(searchFlags))]; self.searchFlags = [aDecoder decodeIntegerForKey:NSStringFromSelector(@selector(searchFlags))];
return self; return self;
} }
@@ -90,7 +90,7 @@
- (void )_updatePreferences { - (void )_updatePreferences {
NSData *myData = [NSKeyedArchiver archivedDataWithRootObject:self]; NSData *myData = [NSKeyedArchiver archivedDataWithRootObject:self];
[[NSUserDefaults standardUserDefaults] setObject:myData forKey:kMPSettingsKeyEntrySearchFilterContext]; [NSUserDefaults.standardUserDefaults setObject:myData forKey:kMPSettingsKeyEntrySearchFilterContext];
[[NSUserDefaults standardUserDefaults] synchronize]; [NSUserDefaults.standardUserDefaults synchronize];
} }
@end @end