diff --git a/External/KCOrderedAccessorFix b/External/KCOrderedAccessorFix
index e1955221..1b8f8b79 160000
--- a/External/KCOrderedAccessorFix
+++ b/External/KCOrderedAccessorFix
@@ -1 +1 @@
-Subproject commit e1955221bf52d53736e7d3e7d38465c509e02562
+Subproject commit 1b8f8b79ad12b70976c7a417ff1a9d29e8c0ed73
diff --git a/External/Pearl b/External/Pearl
index ef628f38..2237aaf4 160000
--- a/External/Pearl
+++ b/External/Pearl
@@ -1 +1 @@
-Subproject commit ef628f388e70459725de21e3528831c705f88795
+Subproject commit 2237aaf4295f74627b63040c4c246cddff339958
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.h b/MasterPassword/ObjC/Mac/MPPasswordWindowController.h
index ee4fdcbd..2bc790f6 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.h
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.h
@@ -29,7 +29,6 @@
@property(nonatomic) BOOL alternatePressed;
@property(nonatomic) BOOL locked;
@property(nonatomic) BOOL newUser;
-@property(nonatomic) BOOL alwaysYes;
@property(nonatomic, weak) IBOutlet NSArrayController *sitesController;
@property(nonatomic, weak) IBOutlet NSImageView *blurView;
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
index 04340736..1c89d976 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
@@ -36,7 +36,7 @@
@end
-@implementation MPPasswordWindowController { BOOL _skipTextChange; }
+@implementation MPPasswordWindowController
#pragma mark - Life
@@ -54,31 +54,33 @@
// }];
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
- [self fadeIn];
- [self updateUser];
- }];
+ [self fadeIn];
+ [self updateUser];
+ }];
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
- NSWindow *sheet = [self.window attachedSheet];
- if (sheet)
- [NSApp endSheet:sheet];
- }];
+ NSWindow *sheet = [self.window attachedSheet];
+ if (sheet)
+ [NSApp endSheet:sheet];
+ }];
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillResignActiveNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
+#ifndef DEBUG
[self fadeOut];
- }];
+#endif
+ }];
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedInNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
- [self updateUser];
- }];
+ [self updateUser];
+ }];
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
- [self updateUser];
- }];
+ [self updateUser];
+ }];
[self observeKeyPath:@"sitesController.selection"
withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
- [_self updateSelection];
- }];
+ [_self updateSelection];
+ }];
NSSearchFieldCell *siteFieldCell = self.siteField.cell;
siteFieldCell.searchButtonCell = nil;
@@ -124,17 +126,15 @@
- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector {
if (control == self.siteField) {
- if ([NSStringFromSelector( commandSelector ) rangeOfString:@"delete"].location == 0) {
- _skipTextChange = YES;
- dbg_return_tr( NO, @, control, NSStringFromSelector( commandSelector ) );
- }
+ if ([NSStringFromSelector( commandSelector ) rangeOfString:@"delete"].location == 0)
+ return NO;
}
if (control == self.securePasswordField || control == self.revealPasswordField) {
if (commandSelector == @selector( insertNewline: ))
- dbg_return_tr( NO, @, control, NSStringFromSelector( commandSelector ) );
+ return NO;
}
- dbg_return_tr( [self handleCommand:commandSelector], @, control, NSStringFromSelector( commandSelector ) );
+ return [self handleCommand:commandSelector];
}
- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor {
@@ -174,7 +174,6 @@
- (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
- dbg( @"textView:%@doCommandBySelector:%@", textView, NSStringFromSelector( commandSelector ) );
return [self handleCommand:commandSelector];
}
@@ -227,9 +226,9 @@
// "Create" button.
[[MPMacAppDelegate get] addSiteNamed:[self.siteField stringValue] completion:
^(MPSiteEntity *site, NSManagedObjectContext *context) {
- if (site)
- PearlMainQueue( ^{ [self updateSites]; } );
- }];
+ if (site)
+ PearlMainQueue( ^{ [self updateSites]; } );
+ }];
break;
}
default:
@@ -305,19 +304,6 @@
#pragma mark - State
-- (NSString *)query {
-
- return [self.siteField.stringValue stringByReplacingCharactersInRange:self.siteField.currentEditor.selectedRange withString:@""]?: @"";
-}
-
-- (BOOL)alwaysYes {
-
- return YES;
-}
-
-- (void)setAlwaysYes:(BOOL)alwaysYes {
-}
-
- (void)insertObject:(MPSiteModel *)model inSitesAtIndex:(NSUInteger)index {
[self.sites insertObject:model atIndex:index];
@@ -347,7 +333,7 @@
[alert addButtonWithTitle:@"Delete"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Delete Site?"];
- [alert setInformativeText:strf( @"Do you want to delete the site named:\n\n%@", self.selectedSite.siteName )];
+ [alert setInformativeText:strf( @"Do you want to delete the site named:\n\n%@", self.selectedSite.name )];
[alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector( alertDidEnd:returnCode:contextInfo: ) contextInfo:MPAlertDeleteSite];
}
@@ -358,7 +344,7 @@
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Login Name"];
- [alert setInformativeText:strf( @"Enter the login name for: %@", self.selectedSite.siteName )];
+ [alert setInformativeText:strf( @"Enter the login name for: %@", self.selectedSite.name )];
NSTextField *loginField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
loginField.stringValue = self.selectedSite.loginName?: @"";
[loginField selectText:self];
@@ -392,7 +378,7 @@
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Password"];
- [alert setInformativeText:strf( @"Enter the new password for: %@", self.selectedSite.siteName )];
+ [alert setInformativeText:strf( @"Enter the new password for: %@", self.selectedSite.name )];
[alert setAccessoryView:[[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]];
[alert layout];
[alert beginSheetModalForWindow:self.window modalDelegate:self
@@ -408,8 +394,9 @@
MPSiteType type = [types[t] unsignedIntegerValue];
NSString *title = [site.algorithm nameOfType:type];
if (type & MPSiteTypeClassGenerated)
- title = [site.algorithm generatePasswordForSiteNamed:site.siteName ofType:type
- withCounter:site.counter usingKey:[MPMacAppDelegate get].key];
+ title = [site.algorithm generatePasswordForSiteNamed:site.name ofType:type
+ withCounter:site.counter
+ usingKey:[MPMacAppDelegate get].key];
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
cell.tag = type;
@@ -421,7 +408,7 @@
[alert addButtonWithTitle:@"Save"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:@"Change Password Type"];
- [alert setInformativeText:strf( @"Choose a new password type for: %@", site.siteName )];
+ [alert setInformativeText:strf( @"Choose a new password type for: %@", site.name )];
[alert setAccessoryView:self.passwordTypesBox];
[alert layout];
[alert beginSheetModalForWindow:self.window modalDelegate:self
@@ -464,9 +451,9 @@
NSUserNotification *notification = [NSUserNotification new];
notification.title = @"Password Copied";
if (selectedSite.loginName.length)
- notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.siteName );
+ notification.subtitle = strf( @"%@ at %@", selectedSite.loginName, selectedSite.name );
else
- notification.subtitle = selectedSite.siteName;
+ notification.subtitle = selectedSite.name;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
}
else {
@@ -515,12 +502,24 @@
return;
}
- NSString *query = [self query];
+ static NSRegularExpression *fuzzyRE;
+ static dispatch_once_t once = 0;
+ dispatch_once( &once, ^{
+ fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
+ } );
+
+ NSString *queryString = self.siteField.stringValue;
+ NSString *queryPattern = [queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1*"];
+ NSMutableArray *fuzzyGroups = [NSMutableArray new];
+ [fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
+ usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+ [fuzzyGroups addObject:[queryString substringWithRange:result.range] ];
+ }];
[MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ];
- fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name BEGINSWITH[cd] %@) AND user == %@",
- query, query, [[MPMacAppDelegate get] activeUserInContext:context]];
+ fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
+ queryPattern, queryPattern, [[MPMacAppDelegate get] activeUserInContext:context]];
NSError *error = nil;
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
@@ -531,36 +530,14 @@
NSMutableArray *newSites = [NSMutableArray arrayWithCapacity:[siteResults count]];
for (MPSiteEntity *site in siteResults)
- [newSites addObject:[[MPSiteModel alloc] initWithEntity:site]];
+ [newSites addObject:[[MPSiteModel alloc] initWithEntity:site fuzzyGroups:fuzzyGroups]];
self.sites = newSites;
}];
}
- (void)updateSelection {
- if (_skipTextChange) {
- _skipTextChange = NO;
- return;
- }
-
- NSString *siteName = self.selectedSite.siteName;
- if (!siteName)
- return;
-
- if ([self.window isKeyWindow] && [self.siteField isEqual:[self.window firstResponder]]) {
- NSRange siteNameQueryRange = [siteName rangeOfString:[self query]];
- self.siteField.stringValue = siteName;
-
- if (siteNameQueryRange.location == 0)
- self.siteField.currentEditor.selectedRange =
- NSMakeRange( siteNameQueryRange.length, siteName.length - siteNameQueryRange.length );
- }
-
[self.siteTable scrollRowToVisible:(NSInteger)self.sitesController.selectionIndex];
- [self updateGradient];
-}
-
-- (void)updateGradient {
NSView *siteScrollView = self.siteTable.superview.superview;
NSRect selectedCellFrame = [self.siteTable frameOfCellAtColumn:0 row:((NSInteger)self.sitesController.selectionIndex)];
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
index d99fcf9a..7df150cc 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib
@@ -48,7 +48,7 @@
-
+
@@ -181,11 +181,11 @@
-
+
-
+
@@ -438,7 +438,7 @@
NSNegateBoolean
-
+
diff --git a/MasterPassword/ObjC/Mac/MPSiteModel.h b/MasterPassword/ObjC/Mac/MPSiteModel.h
index 2a83576e..9d0c561c 100644
--- a/MasterPassword/ObjC/Mac/MPSiteModel.h
+++ b/MasterPassword/ObjC/Mac/MPSiteModel.h
@@ -17,15 +17,17 @@
//
#import
+#import "MPSiteEntity.h"
@class MPSiteEntity;
@interface MPSiteModel : NSObject
-@property (nonatomic) NSString *siteName;
+@property (nonatomic) NSString *name;
+@property (nonatomic) NSAttributedString *displayedName;
@property (nonatomic) MPSiteType type;
@property (nonatomic) NSString *typeName;
@property (nonatomic) NSString *content;
-@property (nonatomic) NSString *contentDisplay;
+@property (nonatomic) NSString *displayedContent;
@property (nonatomic) NSString *loginName;
@property (nonatomic) NSNumber *uses;
@property (nonatomic) NSUInteger counter;
@@ -34,7 +36,7 @@
@property (nonatomic) BOOL generated;
@property (nonatomic) BOOL stored;
-- (id)initWithEntity:(MPSiteEntity *)entity;
+- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups;
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;
- (void)updateContent;
diff --git a/MasterPassword/ObjC/Mac/MPSiteModel.m b/MasterPassword/ObjC/Mac/MPSiteModel.m
index c7dd6ed7..c3e71205 100644
--- a/MasterPassword/ObjC/Mac/MPSiteModel.m
+++ b/MasterPassword/ObjC/Mac/MPSiteModel.m
@@ -28,25 +28,41 @@
BOOL _initialized;
}
-- (id)initWithEntity:(MPSiteEntity *)entity {
+- (id)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
if (!(self = [super init]))
return nil;
- [self setEntity:entity];
+ [self setEntity:entity fuzzyGroups:fuzzyGroups];
_initialized = YES;
return self;
}
-- (void)setEntity:(MPSiteEntity *)entity {
+- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
if ([_entityOID isEqual:entity.objectID])
return;
_entityOID = entity.objectID;
+ NSString *siteName = entity.name;
+ NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName];
+ for (NSUInteger f = 0, s = (NSUInteger)-1; f < [fuzzyGroups count]; ++f) {
+ s = [siteName rangeOfString:fuzzyGroups[f] options:NSDiacriticInsensitiveSearch | NSCaseInsensitiveSearch
+ range:NSMakeRange( s + 1, [siteName length] - (s + 1) )].location;
+ if (s == NSNotFound)
+ break;
+
+ [attributedSiteName addAttribute:NSBackgroundColorAttributeName value:[NSColor alternateSelectedControlColor]
+ range:NSMakeRange( s, [fuzzyGroups[f] length] )];
+ }
+ NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
+ paragraphStyle.alignment = NSCenterTextAlignment;
+ [attributedSiteName addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange( 0, [siteName length] )];
+
+ self.displayedName = attributedSiteName;
+ self.name = siteName;
self.algorithm = entity.algorithm;
- self.siteName = entity.name;
self.lastUsed = entity.lastUsed;
self.type = entity.type;
self.typeName = entity.typeName;
@@ -123,7 +139,7 @@
PearlMainQueue( ^{
self.content = result;
- self.contentDisplay = displayResult;
+ self.displayedContent = displayResult;
} );
}];
[entity resolveLoginUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {