Merge branch 'encapsulate_search'

This commit is contained in:
michael starke
2014-08-27 19:48:15 +02:00
14 changed files with 280 additions and 85 deletions

View File

@@ -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 = "<group>"; };
4C08C3AD17B3022400BBBC95 /* KPKLegacyHeaderWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KPKLegacyHeaderWriter.m; sourceTree = "<group>"; };
4C08C3AF17B3036500BBBC95 /* KPKLegacyFormat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = KPKLegacyFormat.h; path = Format/KPKLegacyFormat.h; sourceTree = "<group>"; };
4C0AF62D195C1F2B009E658D /* MPEntrySearchContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEntrySearchContext.h; sourceTree = "<group>"; };
4C0AF62E195C1F2B009E658D /* MPEntrySearchContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEntrySearchContext.m; sourceTree = "<group>"; };
4C0B038918E36DA400B9F9C9 /* MPFixAutotypeWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixAutotypeWindowController.h; sourceTree = "<group>"; };
4C0B038A18E36DA400B9F9C9 /* MPFixAutotypeWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFixAutotypeWindowController.m; sourceTree = "<group>"; };
4C0B038B18E36DA400B9F9C9 /* FixAutotypeWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FixAutotypeWindow.xib; sourceTree = "<group>"; };
@@ -1366,6 +1369,8 @@
4C94A0711938DDC20040ABAB /* MPDocument+EditingSession.m */,
4C94A06D1938DC8C0040ABAB /* MPEditSession.h */,
4C94A06E1938DC8C0040ABAB /* MPEditSession.m */,
4C0AF62D195C1F2B009E658D /* MPEntrySearchContext.h */,
4C0AF62E195C1F2B009E658D /* MPEntrySearchContext.m */,
);
name = Model;
sourceTree = "<group>";
@@ -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 */,

View File

@@ -7,7 +7,6 @@
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPContextBarViewController">
<connections>
<outlet property="duplicatePasswordsButton" destination="hMB-2T-pBD" id="dWH-3p-fy6"/>
<outlet property="emptyTrashButton" destination="szx-Hx-OrV" id="i1Y-qB-TW3"/>
<outlet property="exitHistoryButton" destination="pqx-su-vAh" id="JmV-vC-F48"/>
<outlet property="filterLabelTextField" destination="6" id="60"/>
@@ -15,6 +14,7 @@
<outlet property="historyLabel" destination="gGR-f0-dcr" id="slx-9D-8k8"/>
<outlet property="notesButton" destination="SaV-5p-jIX" id="sQc-nE-BOP"/>
<outlet property="passwordButton" destination="Uhl-ck-vVQ" id="I35-Nv-6jK"/>
<outlet property="specialFilterPopUpButton" destination="aPQ-t2-bgz" id="5II-Ml-qg1"/>
<outlet property="titleButton" destination="O7W-cn-eUP" id="Vlg-KG-62R"/>
<outlet property="trashBar" destination="DXf-SC-gVG" id="3aZ-Xc-VDk"/>
<outlet property="urlButton" destination="Bn2-iY-mQ3" id="PNG-kv-PQN"/>
@@ -102,31 +102,38 @@
<action selector="toggleSearchFlags:" target="-1" id="cuN-5p-vZD"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hMB-2T-pBD">
<rect key="frame" x="412" y="6" width="144" height="17"/>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aPQ-t2-bgz">
<rect key="frame" x="493" y="5" width="63" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="recessed" title="Duplicate Passwords" bezelStyle="recessed" alignment="center" controlSize="small" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="G1B-En-dc2">
<popUpButtonCell key="cell" type="recessed" title="Item 1" bezelStyle="recessed" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" autoenablesItems="NO" selectedItem="LRm-iZ-XrA" id="faz-pC-uGX">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
<font key="font" metaFont="systemBold" size="12"/>
</buttonCell>
</button>
<menu key="menu" title="OtherViews" autoenablesItems="NO" id="wC4-fF-dLW">
<items>
<menuItem title="Item 1" state="on" id="LRm-iZ-XrA"/>
<menuItem title="Item 2" id="cpr-p6-YAY"/>
<menuItem title="Item 3" id="CFk-71-NYQ"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
</subviews>
<constraints>
<constraint firstAttribute="centerY" secondItem="O7W-cn-eUP" secondAttribute="centerY" id="2uC-wS-HDi"/>
<constraint firstAttribute="centerY" secondItem="hMB-2T-pBD" secondAttribute="centerY" id="4rl-iO-76L"/>
<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 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="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 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 firstAttribute="trailing" secondItem="hMB-2T-pBD" secondAttribute="trailing" constant="20" symbolic="YES" id="jAm-sk-ErW"/>
<constraint firstAttribute="trailing" secondItem="aPQ-t2-bgz" secondAttribute="trailing" constant="20" symbolic="YES" id="XFv-Pv-Bzq"/>
<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 firstItem="Uhl-ck-vVQ" firstAttribute="leading" secondItem="0R1-PX-dgn" secondAttribute="trailing" constant="8" id="u8d-C2-O9h"/>
<constraint firstAttribute="centerY" secondItem="Uhl-ck-vVQ" secondAttribute="centerY" id="uvE-ch-ysK"/>
<constraint firstAttribute="centerY" secondItem="Bn2-iY-mQ3" secondAttribute="centerY" id="xZJ-EZ-Nic"/>
<constraint firstItem="hMB-2T-pBD" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="SaV-5p-jIX" secondAttribute="trailing" constant="8" symbolic="YES" id="zRT-y1-oMu"/>
</constraints>
</customView>
</subviews>
@@ -135,6 +142,8 @@
<constraint firstAttribute="bottom" secondItem="1" secondAttribute="bottom" id="Z4A-sK-v8K"/>
<constraint firstItem="1" firstAttribute="top" secondItem="caQ-XO-RkM" secondAttribute="top" id="a5u-p3-ay5"/>
<constraint firstAttribute="trailing" secondItem="1" secondAttribute="trailing" id="hTR-dg-7Ql"/>
<constraint firstAttribute="trailing" secondItem="1" secondAttribute="trailing" id="mos-cv-3fn"/>
<constraint firstAttribute="trailing" secondItem="1" secondAttribute="trailing" id="rO8-g6-lT3"/>
</constraints>
</view>
</tabViewItem>

View File

@@ -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;

View File

@@ -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

View File

@@ -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 */

View File

@@ -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];
}
- (void)updateSearch:(id)sender {
MPDocumentWindowController *windowController = [self windowControllers][0];
self.searchString = [windowController.searchField stringValue];
if(NO == self.hasSearch) {
[[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidEnterSearchNotification object:self];
#pragma mark Actions
- (void)performFindPanelAction:(id)sender {
[self enterSearchWithContext:[MPEntrySearchContext userContext]];
}
self.hasSearch = YES;
- (void)updateSearch:(id)sender {
if(NO == self.hasSearch) {
[self enterSearchWithContext:[MPEntrySearchContext userContext]];
return; // We get called back!
}
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;

View File

@@ -22,6 +22,7 @@
#import <Cocoa/Cocoa.h>
#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;
/*

View File

@@ -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]) {

View File

@@ -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 <Foundation/Foundation.h>
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 <NSSecureCoding,NSCopying>
/**
* 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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5053" systemVersion="13C64" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13E28" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5053"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPOutlineViewController">

Binary file not shown.