From 060059ff0c193d468b77b5cccb939ae1d380a566 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Mon, 16 May 2016 00:50:17 -0400 Subject: [PATCH] Bugfixes, a gradient backdrop behind bottom buttons and support for generated login names. --- MasterPassword/ObjC/MPAlgorithmV0.m | 10 +- MasterPassword/ObjC/MPAppDelegate_InApp.m | 2 +- MasterPassword/ObjC/Mac/MPGradientView.h | 16 +++ MasterPassword/ObjC/Mac/MPGradientView.m | 81 +++++++++++++ MasterPassword/ObjC/Mac/MPInitialWindow.xib | 11 +- MasterPassword/ObjC/Mac/MPMacAppDelegate.m | 6 + .../ObjC/Mac/MPPasswordWindowController.m | 38 ++++-- .../ObjC/Mac/MPPasswordWindowController.xib | 109 ++++++++++-------- MasterPassword/ObjC/Mac/MPSiteModel.h | 1 + MasterPassword/ObjC/Mac/MPSiteModel.m | 25 ++++ .../project.pbxproj | 6 + 11 files changed, 232 insertions(+), 73 deletions(-) create mode 100644 MasterPassword/ObjC/Mac/MPGradientView.h create mode 100644 MasterPassword/ObjC/Mac/MPGradientView.m diff --git a/MasterPassword/ObjC/MPAlgorithmV0.m b/MasterPassword/ObjC/MPAlgorithmV0.m index 522ea598..bb9dc247 100644 --- a/MasterPassword/ObjC/MPAlgorithmV0.m +++ b/MasterPassword/ObjC/MPAlgorithmV0.m @@ -496,7 +496,7 @@ NSOperationQueue *_mpwQueue = nil; NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." ); NSString *name = site.name; BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins]; - NSString *loginName = loginGenerated? nil: site.loginName; + NSString *loginName = site.loginName; id algorithm = nil; if (!name.length) err( @"Missing name." ); @@ -506,10 +506,8 @@ NSOperationQueue *_mpwQueue = nil; algorithm = site.algorithm; dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ - if (loginGenerated) - resultBlock( [algorithm generateLoginForSiteNamed:name usingKey:siteKey] ); - else - resultBlock( loginName ); + resultBlock( loginName || !loginGenerated? loginName: + [algorithm generateLoginForSiteNamed:name usingKey:siteKey] ); } ); } @@ -611,7 +609,7 @@ NSOperationQueue *_mpwQueue = nil; err( @"Missing name." ); else if (!siteKey) err( @"Missing key." ); - else + else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers]) algorithm = question.site.algorithm; dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ diff --git a/MasterPassword/ObjC/MPAppDelegate_InApp.m b/MasterPassword/ObjC/MPAppDelegate_InApp.m index 822ff2b7..f7c43b7e 100644 --- a/MasterPassword/ObjC/MPAppDelegate_InApp.m +++ b/MasterPassword/ObjC/MPAppDelegate_InApp.m @@ -66,7 +66,7 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve return NO; #if ADHOC || DEBUG - // All features are unlocked for beta / debug versions. + // All features are unlocked for beta / debug / mac versions. return YES; #else // Check if product is purchased. diff --git a/MasterPassword/ObjC/Mac/MPGradientView.h b/MasterPassword/ObjC/Mac/MPGradientView.h new file mode 100644 index 00000000..d1d4e6f0 --- /dev/null +++ b/MasterPassword/ObjC/Mac/MPGradientView.h @@ -0,0 +1,16 @@ +// +// Created by Maarten Billemont on 2016-05-15. +// Copyright (c) 2016 Lyndir. All rights reserved. +// + +#import + +IB_DESIGNABLE +@interface MPGradientView : NSView + +@property(nonatomic, retain) IBInspectable NSColor *startingColor; +@property(nonatomic, retain) IBInspectable NSColor *endingColor; +@property(nonatomic, assign) IBInspectable NSInteger angle; +@property(nonatomic, assign) IBInspectable CGFloat ratio; + +@end diff --git a/MasterPassword/ObjC/Mac/MPGradientView.m b/MasterPassword/ObjC/Mac/MPGradientView.m new file mode 100644 index 00000000..efc41370 --- /dev/null +++ b/MasterPassword/ObjC/Mac/MPGradientView.m @@ -0,0 +1,81 @@ +// +// Created by Maarten Billemont on 2016-05-15. +// Copyright (c) 2016 Lyndir. All rights reserved. +// + +#import "MPGradientView.h" + +@implementation MPGradientView { + NSGradient *gradient; +} + +- (id)initWithFrame:(NSRect)frame { + + if (!(self = [super initWithFrame:frame])) + return nil; + + [self defaults]; + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + + if (!(self = [super initWithCoder:coder])) + return nil; + + [self defaults]; + return self; +} + +- (void)defaults { + + self.startingColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.0]; + self.endingColor = [NSColor colorWithCalibratedWhite:0.0 alpha:1.0]; + self.angle = 270; + self.ratio = 0.5f; +} + +- (void)setStartingColor:(NSColor *)startingColor { + + _startingColor = startingColor; + gradient = nil; + [self setNeedsDisplay:YES]; +} + +- (void)setEndingColor:(NSColor *)endingColor { + + _endingColor = endingColor; + gradient = nil; + [self setNeedsDisplay:YES]; +} + +- (void)setAngle:(NSInteger)angle { + + _angle = angle; + gradient = nil; + [self setNeedsDisplay:YES]; +} + +- (void)setRatio:(CGFloat)ratio { + + _ratio = ratio; + gradient = nil; + [self setNeedsDisplay:YES]; +} + +- (void)drawRect:(NSRect)dirtyRect { + + if (!self.startingColor || !self.endingColor || [self.startingColor isEqual:self.endingColor]) { + [(self.startingColor?: self.endingColor) set]; + NSRectFill( dirtyRect ); + return; + } + + [(gradient?: (gradient = [[NSGradient alloc] initWithColorsAndLocations: + self.startingColor, (CGFloat)0.f, + [self.startingColor blendedColorWithFraction:0.5f ofColor:self.endingColor], self.ratio, + self.endingColor, (CGFloat)1.f, nil])) + drawInRect:self.bounds angle:self.angle]; +} + +@end diff --git a/MasterPassword/ObjC/Mac/MPInitialWindow.xib b/MasterPassword/ObjC/Mac/MPInitialWindow.xib index 259509b7..424e368d 100644 --- a/MasterPassword/ObjC/Mac/MPInitialWindow.xib +++ b/MasterPassword/ObjC/Mac/MPInitialWindow.xib @@ -17,7 +17,7 @@ - + @@ -102,11 +102,8 @@ from anywhere. - - + diff --git a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m index 8c1ccc55..b06a27c5 100644 --- a/MasterPassword/ObjC/Mac/MPMacAppDelegate.m +++ b/MasterPassword/ObjC/Mac/MPMacAppDelegate.m @@ -216,6 +216,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven return NO; } +- (BOOL)isFeatureUnlocked:(NSString *)productIdentifier { + + // All features are unlocked for mac versions. + return YES; +} + #pragma mark - Actions - (void)selectUser:(NSMenuItem *)item { diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m index 406d6ed9..44635a0b 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m @@ -17,7 +17,6 @@ // #import -#import #import "MPPasswordWindowController.h" #import "MPMacAppDelegate.h" #import "MPAppDelegate_Store.h" @@ -260,21 +259,36 @@ [alert addButtonWithTitle:@"Save"]; [alert addButtonWithTitle:@"Cancel"]; [alert setMessageText:@"Change Login Name"]; - [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]; - [alert setAccessoryView:loginField]; + [alert setInformativeText:strf( @"Your login name for: %@", self.selectedSite.name )]; + NSTextField *loginField = [NSTextField new]; + [loginField bind:@"value" toObject:self.selectedSite withKeyPath:@"loginName" options:nil]; + NSButton *generatedField = [NSButton new]; + [generatedField setButtonType:NSSwitchButton]; + [generatedField bind:@"value" toObject:self.selectedSite withKeyPath:@"loginGenerated" options:nil]; + generatedField.title = @"Generated"; + NSStackView *stackView = [NSStackView stackViewWithViews:@[ loginField, generatedField ]]; + stackView.orientation = NSUserInterfaceLayoutOrientationVertical; + stackView.frame = NSMakeRect( 0, 0, 200, 44 ); + [stackView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[loginField(200)]" + options:0 metrics:nil + views:NSDictionaryOfVariableBindings( loginField, stackView )]]; + [stackView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[generatedField(200)]" + options:0 metrics:nil + views:NSDictionaryOfVariableBindings( generatedField, stackView )]]; + [alert setAccessoryView:stackView]; [alert layout]; + [loginField selectText:self]; + [alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) { switch (returnCode) { case NSAlertFirstButtonReturn: { // "Save" button. - NSString *loginName = [(NSSecureTextField *)alert.accessoryView stringValue]; + NSString *loginName = [loginField stringValue]; [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { MPSiteEntity *entity = [self.selectedSite entityInContext:context]; - entity.loginName = loginName; + entity.loginName = !self.selectedSite.loginGenerated && [loginName length]? loginName: nil; [context saveToStore]; + [self.selectedSite updateContent]; }]; break; } @@ -311,7 +325,7 @@ alert_.messageText = @"Master Password Reset"; alert_.informativeText = strf( @"%@'s master password has been reset.\n\nYou can now set a new one by logging in.", activeUserName ); - [alert_ beginSheetModalForWindow:self.window modalDelegate:nil didEndSelector:NULL contextInfo:nil]; + [alert_ beginSheetModalForWindow:self.window completionHandler:nil]; if ([MPMacAppDelegate get].key) [[MPMacAppDelegate get] signOutAnimated:YES]; @@ -334,17 +348,19 @@ [alert addButtonWithTitle:@"Cancel"]; [alert setMessageText:@"Change Password"]; [alert setInformativeText:strf( @"Enter the new password for: %@", self.selectedSite.name )]; - [alert setAccessoryView:[[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]]; + NSSecureTextField *passwordField = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )]; + [alert setAccessoryView:passwordField]; [alert layout]; [alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) { switch (returnCode) { case NSAlertFirstButtonReturn: { // "Save" button. - NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue]; + NSString *password = [passwordField stringValue]; [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { MPSiteEntity *entity = [self.selectedSite entityInContext:context]; [entity.algorithm savePassword:password toSite:entity usingKey:[MPMacAppDelegate get].key]; [context saveToStore]; + [self.selectedSite updateContent]; }]; break; } diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib index cfc91f6a..a3ae8aa7 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.xib @@ -29,20 +29,20 @@ - - + + - + - + - + @@ -217,17 +217,17 @@ - + - + - - + + - + @@ -311,8 +311,22 @@ + + + + + + + + + + + + + + - + @@ -346,7 +360,7 @@ - + @@ -358,7 +372,7 @@ - + @@ -393,7 +407,7 @@ - + @@ -432,7 +446,7 @@ - + @@ -507,7 +521,7 @@ - + @@ -555,15 +569,15 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit. - - + + - - - + + @@ -624,7 +633,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit. - + @@ -733,7 +742,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit. - + @@ -746,7 +755,7 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit. - + @@ -824,8 +833,8 @@ Use the arrows ⇅ to navigate the list or esc ⎋ to exit. - -